feat: 摸金风向标web前端代码初始化

master
laixingyu 3 years ago
commit 40330461ac

@ -0,0 +1,14 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

@ -0,0 +1,5 @@
# just a flag
ENV = 'development'
# base api
VUE_APP_BASE_API = 'https://gupiao.365rise.top/ssdmn-gupiao-api'

@ -0,0 +1,5 @@
# just a flag
ENV = 'production'
# base api
VUE_APP_BASE_API = 'https://gupiao.365rise.top/ssdmn-gupiao-api'

@ -0,0 +1,8 @@
NODE_ENV = production
# just a flag
ENV = 'staging'
# base api
VUE_APP_BASE_API = '/stage-api'

@ -0,0 +1,4 @@
build/*.js
src/assets
public
dist

@ -0,0 +1,198 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true,
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
rules: {
"vue/max-attributes-per-line": [2, {
"singleline": 10,
"multiline": {
"max": 1,
"allowFirstLine": false
}
}],
"vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline":"off",
"vue/name-property-casing": ["error", "PascalCase"],
"vue/no-v-html": "off",
'accessor-pairs': 2,
'arrow-spacing': [2, {
'before': true,
'after': true
}],
'block-spacing': [2, 'always'],
'brace-style': [2, '1tbs', {
'allowSingleLine': true
}],
'camelcase': [0, {
'properties': 'always'
}],
'comma-dangle': [2, 'never'],
'comma-spacing': [2, {
'before': false,
'after': true
}],
'comma-style': [2, 'last'],
'constructor-super': 2,
'curly': [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
'eqeqeq': ["error", "always", {"null": "ignore"}],
'generator-star-spacing': [2, {
'before': true,
'after': true
}],
'handle-callback-err': [2, '^(err|error)$'],
'indent': [2, 2, {
'SwitchCase': 1
}],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [2, {
'beforeColon': false,
'afterColon': true
}],
'keyword-spacing': [2, {
'before': true,
'after': true
}],
'new-cap': [2, {
'newIsCap': true,
'capIsNew': false
}],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [2, {
'allowLoop': false,
'allowSwitch': false
}],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [2, {
'max': 1
}],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [2, {
'defaultAssignment': false
}],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [2, {
'vars': 'all',
'args': 'none'
}],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [2, {
'initialized': 'never'
}],
'operator-linebreak': [2, 'after', {
'overrides': {
'?': 'before',
':': 'before'
}
}],
'padded-blocks': [2, 'never'],
'quotes': [2, 'single', {
'avoidEscape': true,
'allowTemplateLiterals': true
}],
'semi': [2, 'never'],
'semi-spacing': [2, {
'before': false,
'after': true
}],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [2, {
'words': true,
'nonwords': false
}],
'spaced-comment': [2, 'always', {
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
'yoda': [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [2, 'always', {
objectsInObjects: false
}],
'array-bracket-spacing': [2, 'never']
}
}

16
.gitignore vendored

@ -0,0 +1,16 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

@ -0,0 +1,5 @@
language: node_js
node_js: 10
script: npm run test
notifications:
email: false

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present PanJiaChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,111 @@
# vue-admin-template
> 这是一个极简的 vue admin 管理后台。它只包含了 Element UI & axios & iconfont & permission control & lint这些搭建后台必要的东西。
[线上地址](http://panjiachen.github.io/vue-admin-template)
[国内访问](https://panjiachen.gitee.io/vue-admin-template)
目前版本为 `v4.0+` 基于 `vue-cli` 进行构建,若你想使用旧版本,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-admin-template/tree/tag/3.11.0),它不依赖 `vue-cli`
<p align="center">
<b>SPONSORED BY</b>
</p>
<p align="center">
<a href="https://finclip.com?from=vue_element" title="FinClip" target="_blank">
<img height="200px" src="https://gitee.com/panjiachen/gitee-cdn/raw/master/vue%E8%B5%9E%E5%8A%A9.png" title="FinClip">
</a>
</p>
## Extra
如果你想要根据用户角色来动态生成侧边栏和 router你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
## 相关项目
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
## Build Setup
```bash
# 克隆项目
git clone https://github.com/PanJiaChen/vue-admin-template.git
# 进入项目目录
cd vue-admin-template
# 安装依赖
npm install
# 建议不要直接使用 cnpm 安装以来,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 启动服务
npm run dev
```
浏览器访问 [http://localhost:9528](http://localhost:9528)
## 发布
```bash
# 构建测试环境
npm run build:stage
# 构建生产环境
npm run build:prod
```
## 其它
```bash
# 预览发布环境效果
npm run preview
# 预览发布环境效果 + 静态资源分析
npm run preview -- --report
# 代码格式检查
npm run lint
# 代码格式检查并自动修复
npm run lint -- --fix
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
## 购买贴纸
你也可以通过 购买[官方授权的贴纸](https://smallsticker.com/product/vue-element-admin) 的方式来支持 vue-element-admin - 每售出一张贴纸,我们将获得 2 元的捐赠。
## Demo
![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License
[MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
Copyright (c) 2017-present PanJiaChen

@ -0,0 +1,99 @@
# vue-admin-template
English | [简体中文](./README-zh.md)
> A minimal vue admin template with Element UI & axios & iconfont & permission control & lint
**Live demo:** http://panjiachen.github.io/vue-admin-template
**The current version is `v4.0+` build on `vue-cli`. If you want to use the old version , you can switch branch to [tag/3.11.0](https://github.com/PanJiaChen/vue-admin-template/tree/tag/3.11.0), it does not rely on `vue-cli`**
<p align="center">
<b>SPONSORED BY</b>
</p>
<p align="center">
<a href="https://finclip.com?from=vue_element" title="FinClip" target="_blank">
<img height="200px" src="https://gitee.com/panjiachen/gitee-cdn/raw/master/vue%E8%B5%9E%E5%8A%A9.png" title="FinClip">
</a>
</p>
## Build Setup
```bash
# clone the project
git clone https://github.com/PanJiaChen/vue-admin-template.git
# enter the project directory
cd vue-admin-template
# install dependency
npm install
# develop
npm run dev
```
This will automatically open http://localhost:9528
## Build
```bash
# build for test environment
npm run build:stage
# build for production environment
npm run build:prod
```
## Advanced
```bash
# preview the release environment effect
npm run preview
# preview the release environment effect + static resource analysis
npm run preview -- --report
# code format check
npm run lint
# code format check and auto fix
npm run lint -- --fix
```
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
## Demo
![demo](https://github.com/PanJiaChen/PanJiaChen.github.io/blob/master/images/demo.gif)
## Extra
If you want router permission && generate menu by user roles , you can use this branch [permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
For `typescript` version, you can use [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
## Related Project
- [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License
[MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
Copyright (c) 2017-present PanJiaChen

@ -0,0 +1,14 @@
module.exports = {
presets: [
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
'@vue/cli-plugin-babel/preset'
],
'env': {
'development': {
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
// https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html
'plugins': ['dynamic-import-node']
}
}
}

@ -0,0 +1,35 @@
const { run } = require('runjs')
const chalk = require('chalk')
const config = require('../vue.config.js')
const rawArgv = process.argv.slice(2)
const args = rawArgv.join(' ')
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
const report = rawArgv.includes('--report')
run(`vue-cli-service build ${args}`)
const port = 9526
const publicPath = config.publicPath
var connect = require('connect')
var serveStatic = require('serve-static')
const app = connect()
app.use(
publicPath,
serveStatic('./dist', {
index: ['index.html', '/']
})
)
app.listen(port, function () {
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
if (report) {
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
}
})
} else {
run(`vue-cli-service build ${args}`)
}

Binary file not shown.

@ -0,0 +1,24 @@
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: ['jest-serializer-vue'],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
coverageDirectory: '<rootDir>/tests/unit/coverage',
// 'collectCoverage': true,
'coverageReporters': [
'lcov',
'text-summary'
],
testURL: 'http://localhost/'
}

@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

@ -0,0 +1,57 @@
const Mock = require('mockjs')
const { param2Obj } = require('./utils')
const user = require('./user')
const table = require('./table')
const mocks = [
...user,
...table
]
// for front mock
// please use it cautiously, it will redefine XMLHttpRequest,
// which will cause many of your third-party libraries to be invalidated(like progress event).
function mockXHR() {
// mock patch
// https://github.com/nuysoft/Mock/issues/300
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
Mock.XHR.prototype.send = function() {
if (this.custom.xhr) {
this.custom.xhr.withCredentials = this.withCredentials || false
if (this.responseType) {
this.custom.xhr.responseType = this.responseType
}
}
this.proxy_send(...arguments)
}
function XHR2ExpressReqWrap(respond) {
return function(options) {
let result = null
if (respond instanceof Function) {
const { body, type, url } = options
// https://expressjs.com/en/4x/api.html#req
result = respond({
method: type,
body: JSON.parse(body),
query: param2Obj(url)
})
} else {
result = respond
}
return Mock.mock(result)
}
}
for (const i of mocks) {
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
}
}
module.exports = {
mocks,
mockXHR
}

@ -0,0 +1,81 @@
const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const Mock = require('mockjs')
const mockDir = path.join(process.cwd(), 'mock')
function registerRoutes(app) {
let mockLastIndex
const { mocks } = require('./index.js')
const mocksForServer = mocks.map(route => {
return responseFake(route.url, route.type, route.response)
})
for (const mock of mocksForServer) {
app[mock.type](mock.url, mock.response)
mockLastIndex = app._router.stack.length
}
const mockRoutesLength = Object.keys(mocksForServer).length
return {
mockRoutesLength: mockRoutesLength,
mockStartIndex: mockLastIndex - mockRoutesLength
}
}
function unregisterRoutes() {
Object.keys(require.cache).forEach(i => {
if (i.includes(mockDir)) {
delete require.cache[require.resolve(i)]
}
})
}
// for mock server
const responseFake = (url, type, respond) => {
return {
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
type: type || 'get',
response(req, res) {
console.log('request invoke:' + req.path)
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
}
}
}
module.exports = app => {
// parse app.body
// https://expressjs.com/en/4x/api.html#req.body
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: true
}))
const mockRoutes = registerRoutes(app)
var mockRoutesLength = mockRoutes.mockRoutesLength
var mockStartIndex = mockRoutes.mockStartIndex
// watch files, hot reload mock server
chokidar.watch(mockDir, {
ignored: /mock-server/,
ignoreInitial: true
}).on('all', (event, path) => {
if (event === 'change' || event === 'add') {
try {
// remove mock routes stack
app._router.stack.splice(mockStartIndex, mockRoutesLength)
// clear routes cache
unregisterRoutes()
const mockRoutes = registerRoutes(app)
mockRoutesLength = mockRoutes.mockRoutesLength
mockStartIndex = mockRoutes.mockStartIndex
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`))
} catch (error) {
console.log(chalk.redBright(error))
}
}
})
}

@ -0,0 +1,29 @@
const Mock = require('mockjs')
const data = Mock.mock({
'items|30': [{
id: '@id',
title: '@sentence(10, 20)',
'status|1': ['published', 'draft', 'deleted'],
author: 'name',
display_time: '@datetime',
pageviews: '@integer(300, 5000)'
}]
})
module.exports = [
{
url: '/vue-admin-template/table/list',
type: 'get',
response: config => {
const items = data.items
return {
code: 20000,
data: {
total: items.length,
items: items
}
}
}
}
]

@ -0,0 +1,84 @@
const tokens = {
admin: {
token: 'admin-token'
},
editor: {
token: 'editor-token'
}
}
const users = {
'admin-token': {
roles: ['admin'],
introduction: 'I am a super administrator',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Super Admin'
},
'editor-token': {
roles: ['editor'],
introduction: 'I am an editor',
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
name: 'Normal Editor'
}
}
module.exports = [
// user login
{
url: '/vue-admin-template/user/login',
type: 'post',
response: config => {
const { username } = config.body
const token = tokens[username]
// mock error
if (!token) {
return {
code: 60204,
message: 'Account and password are incorrect.'
}
}
return {
code: 20000,
data: token
}
}
},
// get user info
{
url: '/vue-admin-template/user/info\.*',
type: 'get',
response: config => {
const { token } = config.query
const info = users[token]
// mock error
if (!info) {
return {
code: 50008,
message: 'Login failed, unable to get user details.'
}
}
return {
code: 20000,
data: info
}
}
},
// user logout
{
url: '/vue-admin-template/user/logout',
type: 'post',
response: _ => {
return {
code: 20000,
data: 'success'
}
}
}
]

@ -0,0 +1,25 @@
/**
* @param {string} url
* @returns {Object}
*/
function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) {
return {}
}
const obj = {}
const searchArr = search.split('&')
searchArr.forEach(v => {
const index = v.indexOf('=')
if (index !== -1) {
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
obj[name] = val
}
})
return obj
}
module.exports = {
param2Obj
}

@ -0,0 +1,68 @@
{
"name": "vue-admin-template",
"version": "4.4.0",
"description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
"author": "Pan <panfree23@gmail.com>",
"scripts": {
"dev": "vue-cli-service serve",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"lint": "eslint --ext .js,.vue src",
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit"
},
"dependencies": {
"@qiun/vue-ucharts": "^2.5.0-20230101",
"axios": "0.18.1",
"core-js": "3.6.5",
"echarts": "^5.4.2",
"element-ui": "2.13.2",
"form-data": "^4.0.0",
"js-cookie": "2.2.0",
"md5": "^2.3.0",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"vue": "2.6.10",
"vue-cropper": "^0.5.10",
"vue-print-nb": "^1.7.5",
"vue-router": "3.0.6",
"vuex": "3.1.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.4",
"@vue/cli-plugin-eslint": "4.4.4",
"@vue/cli-plugin-unit-jest": "4.4.4",
"@vue/cli-service": "4.4.4",
"@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "9.5.1",
"babel-eslint": "10.1.0",
"babel-jest": "23.6.0",
"babel-plugin-dynamic-import-node": "2.3.3",
"chalk": "2.4.2",
"connect": "3.6.6",
"eslint": "6.7.2",
"eslint-plugin-vue": "6.2.2",
"html-webpack-plugin": "3.2.0",
"mockjs": "^1.0.1-beta3",
"runjs": "4.3.2",
"sass": "1.26.8",
"sass-loader": "8.0.2",
"script-ext-html-webpack-plugin": "2.1.3",
"serve-static": "1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue-template-compiler": "2.6.10"
},
"browserslist": [
"> 1%",
"last 2 versions"
],
"engines": {
"node": ">=8.9",
"npm": ">= 3.0.0"
},
"license": "MIT"
}

@ -0,0 +1,8 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
'plugins': {
// to edit target browsers: use "browserslist" field in package.json
'autoprefixer': {}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>logo.png">
<title><%= webpackConfig.name %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@ -0,0 +1,16 @@
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: "App",
data() {
return {};
},
created() {},
methods: {},
};
</script>

@ -0,0 +1,103 @@
import request from "@/utils/request";
import md5 from "md5";
function sortParams(params) {
return Object.keys(params)
.filter((k) => params[k] !== "") // 过滤空参数
.sort() // 排序
.reduce((obj, key) => {
obj[key] = params[key];
return obj;
}, {});
}
function sort_ASCII(obj) {
var arr = new Array();
var num = 0;
for (let i in obj) {
arr[num] = i;
num++;
}
var sortArr = arr.sort();
var sortObj = {};
for (let i in sortArr) {
sortObj[sortArr[i]] = obj[sortArr[i]];
}
return sortObj;
}
/**
* 排序参数并生成签名
*/
function generateSign(params = {}) {
// 随机字符串
const nonce_str = Math.random().toString(36).substr(2);
// 排序参数并转 URL 键值对
const strTemp = sortParams(Object.assign(params, { nonce_str }));
console.log(strTemp);
let arr = sort_ASCII(strTemp);
let str = "";
for (let key in arr) {
if (arr[key] || arr[key] === 0) {
str = str + key + "=" + arr[key] + "&";
}
}
console.log(str + "appKey=1f26fe1a6a36daf7651d6437bb6a5fa6");
// 生成 MD5 签名
let sign = md5(str + "appKey=1f26fe1a6a36daf7651d6437bb6a5fa6").toUpperCase();
// const sign = md5(
// `${strTemp}&appKey=1f26fe1a6a36daf7651d6437bb6a5fa6`
// ).toUpperCase();
return {
...params,
nonce_str,
sign,
};
}
// 获取店铺满减
export function getStoreDecrement(data) {
return request({
url: "/getStoreDecrement",
method: "post",
data: generateSign(data),
});
}
// 获取店铺优惠券
export function getStoreCoupon(data) {
return request({
url: "/getStoreCoupon",
method: "post",
data: generateSign(data),
});
}
// 保存店铺优惠券
export function saveStoreCoupon(data) {
return request({
url: "/saveStoreCoupon",
method: "post",
data: generateSign(data),
});
}
// 删除店铺优惠券
export function delStoreCoupon(data) {
return request({
url: "/delStoreCoupon",
method: "post",
data: generateSign(data),
});
}
// 保存店铺满减
export function saveStoreDecrement(data) {
return request({
url: "/saveStoreDecrement",
method: "post",
data: generateSign(data),
});
}
// 删除店铺满减
export function delStoreDecrement(data) {
return request({
url: "/delStoreDecrement",
method: "post",
data: generateSign(data),
});
}

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function getList(params) {
return request({
url: '/vue-admin-template/table/list',
method: 'get',
params
})
}

@ -0,0 +1,257 @@
import request from "@/utils/request";
import md5 from "md5";
function sortParams(params) {
return Object.keys(params)
.filter((k) => params[k] !== "") // 过滤空参数
.sort() // 排序
.reduce((obj, key) => {
obj[key] = params[key];
return obj;
}, {});
}
function sort_ASCII(obj) {
var arr = new Array();
var num = 0;
for (let i in obj) {
arr[num] = i;
num++;
}
var sortArr = arr.sort();
var sortObj = {};
for (let i in sortArr) {
sortObj[sortArr[i]] = obj[sortArr[i]];
}
return sortObj;
}
/**
* 排序参数并生成签名
*/
function generateSign(params = {}) {
// 随机字符串
const nonce_str = Math.random().toString(36).substr(2);
// 排序参数并转 URL 键值对
const strTemp = sortParams(Object.assign(params, { nonce_str }));
console.log(strTemp);
let arr = sort_ASCII(strTemp);
let str = "";
for (let key in arr) {
if (arr[key] || arr[key] === 0) {
str = str + key + "=" + arr[key] + "&";
}
}
console.log(str + "appKey=1f26fe1a6a36daf7651d6437bb6a5fa6");
// 生成 MD5 签名
let sign = md5(str + "appKey=1f26fe1a6a36daf7651d6437bb6a5fa6").toUpperCase();
// const sign = md5(
// `${strTemp}&appKey=1f26fe1a6a36daf7651d6437bb6a5fa6`
// ).toUpperCase();
return {
...params,
nonce_str,
sign,
};
}
// 登录
export function login(data) {
return request({
url: "/user/login/smsCodeLogin",
method: "post",
data,
});
}
// 趋势板块
export function trendPlate(data) {
return request({
url: "/stock/trendPlate",
method: "post",
data,
});
}
// 个股涨跌分页查询
export function riseLossesDetail(data) {
return request({
url: "/stock/riseLossesDetail",
method: "post",
data,
});
}
// 涨跌统计
export function riseLossesStatistics(data) {
return request({
url: "/stock/riseLossesStatistics",
method: "post",
data,
});
}
// 个股详情
export function stockDetails(data) {
return request({
url: "/stock/stockDetails",
method: "post",
data,
});
}
// 个股
export function originalIssueStockDetail(data) {
return request({
url: "/stock/originalIssueStockDetail",
method: "post",
data,
});
}
// 个股详情
export function getByCode(data) {
return request({
url: "/stock/getByCode",
method: "post",
data,
});
}
// 个股排名变化
export function stockSortChange(id) {
return request({
url: "/stock/stockSortChange/" + id,
method: "get",
});
}
// 交易记录
export function findPage(data) {
return request({
url: "/transaction/findPage",
method: "post",
data,
});
}
// 批量删除交易记录
export function delByIds(data) {
return request({
url: "/transaction/delByIds",
method: "post",
data,
});
}
// 查看交易详情
export function getDetailById(id) {
return request({
url: "/transaction/getDetailById/" + id,
method: "get",
});
}
// 保存或修改交易记录
export function saveOrUpdate(data) {
return request({
url: "/transaction/saveOrUpdate",
method: "post",
data,
});
}
export function kLine(data) {
return request({
url: "/stock/kLine",
method: "post",
data,
});
}
// 交易统计盈利、亏损
export function profitOrLoss(data) {
return request({
url: "/transaction/profitOrLoss",
method: "post",
data,
});
}
// 交易统计收益率
export function totalEarnings(data) {
return request({
url: "/transaction/totalEarnings",
method: "post",
data,
});
}
// 收益折线图
export function earningsLine(data) {
return request({
url: "/transaction/earningsLine",
method: "post",
data,
});
}
// 收藏数据
export function query(data) {
return request({
url: "/collect/query",
method: "post",
data,
});
}
// 收藏
export function addQuery(data) {
return request({
url: "/collect/add",
method: "post",
data,
});
}
// 取消收藏
export function delQuery(data) {
return request({
url: "/collect/del",
method: "post",
data,
});
}
// 查询是否收藏某个code
export function queryIsCollect(data) {
return request({
url: "/collect/queryIsCollect",
method: "post",
data,
});
}
// 获取验证码
export function sendSms(data) {
return request({
url: "/login/sendSms",
method: "post",
data,
});
}
// 板块排名变化
export function dongCaiSortChange(id) {
return request({
url: "/stock/dongCaiSortChange/" + id,
method: "get",
});
}
// 板块涨跌区域统计
export function ChangeriseLossesStatistics(data) {
return request({
url: "/stock/dongCaiSortChange/riseLossesStatistics" + data,
method: "get",
});
}
// 板块指数k线图
export function dongCaiSortChangekLine(id) {
return request({
url: "/stock/dongCaiSortChange/kLine/" + id,
method: "get",
});
}
//
// 板块涨跌停个股
export function ChangedongCaiRiseLosses(data) {
return request({
url: "/stock/dongCaiSortChange/dongCaiRiseLosses" + data,
method: "get",
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

@ -0,0 +1,44 @@
<template>
<div style="padding: 0 15px;" @click="toggleClick">
<svg
:class="{'is-active':isActive}"
class="hamburger"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
>
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
</svg>
</div>
</template>
<script>
export default {
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
}
},
methods: {
toggleClick() {
this.$emit('toggleClick')
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
vertical-align: middle;
width: 20px;
height: 20px;
}
.hamburger.is-active {
transform: rotate(180deg);
}
</style>

@ -0,0 +1,62 @@
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return isExternal(this.iconClass)
},
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon() {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>

@ -0,0 +1,9 @@
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component
// register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)

@ -0,0 +1 @@
<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg"><path d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm13.714-31.801c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM71.714 65.98l7.215-27.116c.285-1.23.107-2.378-.536-3.443-.643-1.064-1.56-1.762-2.75-2.094-1.19-.33-2.333-.177-3.429.462-1.095.639-1.81 1.573-2.143 2.804l-7.214 27.116c-2.857.237-5.405 1.266-7.643 3.088-2.238 1.822-3.738 4.152-4.5 6.992-.952 3.644-.476 7.098 1.429 10.364 1.905 3.265 4.69 5.37 8.357 6.317 3.667.947 7.143.474 10.429-1.42 3.285-1.892 5.404-4.66 6.357-8.305.762-2.84.619-5.607-.429-8.305-1.047-2.697-2.762-4.85-5.143-6.46zm47.143-2.342c0-2.508-.893-4.65-2.678-6.424-1.786-1.775-3.94-2.662-6.465-2.662-2.524 0-4.678.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.786 1.775 3.94 2.662 6.464 2.662 2.524 0 4.679-.887 6.465-2.662 1.785-1.775 2.678-3.916 2.678-6.424zm-45.714-45.43c0-2.509-.893-4.65-2.679-6.425C68.68 10.01 66.524 9.122 64 9.122c-2.524 0-4.679.887-6.464 2.661-1.786 1.775-2.679 3.916-2.679 6.425 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zm32 13.629c0-2.508-.893-4.65-2.679-6.424-1.785-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.464 2.662-1.786 1.774-2.679 3.916-2.679 6.424 0 2.508.893 4.65 2.679 6.424 1.785 1.774 3.94 2.662 6.464 2.662 2.524 0 4.679-.888 6.464-2.662 1.786-1.775 2.679-3.916 2.679-6.424zM128 63.638c0 12.351-3.357 23.78-10.071 34.286-.905 1.372-2.19 2.058-3.858 2.058H13.93c-1.667 0-2.953-.686-3.858-2.058C3.357 87.465 0 76.037 0 63.638c0-8.613 1.69-16.847 5.071-24.703C8.452 31.08 13 24.312 18.714 18.634c5.715-5.68 12.524-10.199 20.429-13.559C47.048 1.715 55.333.035 64 .035c8.667 0 16.952 1.68 24.857 5.04 7.905 3.36 14.714 7.88 20.429 13.559 5.714 5.678 10.262 12.446 13.643 20.301 3.38 7.856 5.071 16.09 5.071 24.703z"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M96.258 57.462h31.421C124.794 27.323 100.426 2.956 70.287.07v31.422a32.856 32.856 0 0 1 25.971 25.97zm-38.796-25.97V.07C27.323 2.956 2.956 27.323.07 57.462h31.422a32.856 32.856 0 0 1 25.97-25.97zm12.825 64.766v31.421c30.46-2.885 54.507-27.253 57.713-57.712H96.579c-2.886 13.466-13.146 23.726-26.292 26.291zM31.492 70.287H.07c2.886 30.46 27.253 54.507 57.713 57.713V96.579c-13.466-2.886-23.726-13.146-26.291-26.292z"/></svg>

After

Width:  |  Height:  |  Size: 497 B

@ -0,0 +1 @@
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="128" height="128"><defs><style/></defs><path d="M512 128q69.675 0 135.51 21.163t115.498 54.997 93.483 74.837 73.685 82.006 51.67 74.837 32.17 54.827L1024 512q-2.347 4.992-6.315 13.483T998.87 560.17t-31.658 51.669-44.331 59.99-56.832 64.34-69.504 60.16-82.347 51.5-94.848 34.687T512 896q-69.675 0-135.51-21.163t-115.498-54.826-93.483-74.326-73.685-81.493-51.67-74.496-32.17-54.997L0 513.707q2.347-4.992 6.315-13.483t18.816-34.816 31.658-51.84 44.331-60.33 56.832-64.683 69.504-60.331 82.347-51.84 94.848-34.816T512 128.085zm0 85.333q-46.677 0-91.648 12.331t-81.152 31.83-70.656 47.146-59.648 54.485-48.853 57.686-37.675 52.821-26.325 43.99q12.33 21.674 26.325 43.52t37.675 52.351 48.853 57.003 59.648 53.845T339.2 767.02t81.152 31.488T512 810.667t91.648-12.331 81.152-31.659 70.656-46.848 59.648-54.186 48.853-57.344 37.675-52.651T927.957 512q-12.33-21.675-26.325-43.648t-37.675-52.65-48.853-57.345-59.648-54.186-70.656-46.848-81.152-31.659T512 213.334zm0 128q70.656 0 120.661 50.006T682.667 512 632.66 632.661 512 682.667 391.339 632.66 341.333 512t50.006-120.661T512 341.333zm0 85.334q-35.328 0-60.33 25.002T426.666 512t25.002 60.33T512 597.334t60.33-25.002T597.334 512t-25.002-60.33T512 426.666z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1 @@
<svg width="128" height="64" xmlns="http://www.w3.org/2000/svg"><path d="M127.072 7.994c1.37-2.208.914-5.152-.914-6.87-2.056-1.717-4.797-1.226-6.396.982-.229.245-25.586 32.382-55.74 32.382-29.24 0-55.74-32.382-55.968-32.627-1.6-1.963-4.57-2.208-6.397-.49C-.17 3.086-.399 6.275 1.2 8.238c.457.736 5.94 7.36 14.62 14.72L4.17 35.96c-1.828 1.963-1.6 5.152.228 6.87.457.98 1.6 1.471 2.742 1.471s2.284-.49 3.198-1.472l12.564-13.983c5.94 4.416 13.021 8.587 20.788 11.53l-4.797 17.418c-.685 2.699.686 5.397 3.198 6.133h1.37c2.057 0 3.884-1.472 4.341-3.68L52.6 42.83c3.655.736 7.538 1.227 11.422 1.227 3.883 0 7.767-.49 11.422-1.227l4.797 17.173c.457 2.208 2.513 3.68 4.34 3.68.457 0 .914 0 1.143-.246 2.513-.736 3.883-3.434 3.198-6.133l-4.797-17.172c7.767-2.944 14.848-7.114 20.788-11.53l12.336 13.738c.913.981 2.056 1.472 3.198 1.472s2.284-.49 3.198-1.472c1.828-1.963 1.828-4.906.228-6.87l-11.65-13.001c9.366-7.36 14.849-14.474 14.849-14.474z"/></svg>

After

Width:  |  Height:  |  Size: 944 B

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M84.068 23.784c-1.02 0-1.877-.32-2.572-.96a8.588 8.588 0 0 1-1.738-2.237 11.524 11.524 0 0 1-1.042-2.621c-.232-.895-.348-1.641-.348-2.238V0h.278c.834 0 1.622.085 2.363.256.742.17 1.645.575 2.711 1.214 1.066.64 2.363 1.535 3.892 2.686 1.53 1.15 3.453 2.664 5.77 4.54 2.502 2.045 4.494 3.771 5.977 5.178 1.483 1.406 2.618 2.6 3.406 3.58.787.98 1.274 1.812 1.46 2.494.185.682.277 1.278.277 1.79v2.046H84.068zM127.3 84.01c.278.682.464 1.535.556 2.558.093 1.023-.37 2.003-1.39 2.94-.463.427-.88.832-1.25 1.215-.372.384-.696.704-.974.96a6.69 6.69 0 0 1-.973.767l-11.816-10.741a44.331 44.331 0 0 0 1.877-1.535 31.028 31.028 0 0 1 1.737-1.406c1.112-.938 2.317-1.343 3.615-1.215 1.297.128 2.363.405 3.197.83.927.427 1.923 1.173 2.989 2.239 1.065 1.065 1.876 2.195 2.432 3.388zM78.23 95.902c2.038 0 3.752-.511 5.143-1.534l-26.969 25.83H18.037c-1.761 0-3.684-.47-5.77-1.407a24.549 24.549 0 0 1-5.838-3.709 21.373 21.373 0 0 1-4.518-5.306c-1.204-2.003-1.807-4.07-1.807-6.202V16.495c0-1.79.44-3.665 1.32-5.626A18.41 18.41 0 0 1 5.04 5.562a21.798 21.798 0 0 1 5.213-3.964C12.198.533 14.237 0 16.37 0h53.24v15.984c0 1.62.278 3.367.834 5.242a16.704 16.704 0 0 0 2.572 5.179c1.159 1.577 2.665 2.898 4.518 3.964 1.853 1.066 4.078 1.598 6.673 1.598h20.295v42.325L85.458 92.45c1.02-1.364 1.529-2.856 1.529-4.476 0-2.216-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1c-2.409 0-4.448.789-6.116 2.366-1.668 1.577-2.502 3.474-2.502 5.69 0 2.217.834 4.092 2.502 5.626 1.668 1.535 3.707 2.302 6.117 2.302h52.13zM26.1 47.951c-2.41 0-4.449.789-6.117 2.366-1.668 1.577-2.502 3.473-2.502 5.69 0 2.216.834 4.092 2.502 5.626 1.668 1.534 3.707 2.302 6.117 2.302h52.13c2.409 0 4.47-.768 6.185-2.302 1.715-1.534 2.572-3.41 2.572-5.626 0-2.217-.857-4.113-2.572-5.69-1.714-1.577-3.776-2.366-6.186-2.366H26.1zm52.407 64.063l1.807-1.663 3.476-3.196a479.75 479.75 0 0 0 4.587-4.284 500.757 500.757 0 0 1 5.004-4.667c3.985-3.666 8.48-7.758 13.485-12.276l11.677 10.741-13.485 12.404-5.004 4.603-4.587 4.22a179.46 179.46 0 0 0-3.267 3.068c-.88.853-1.367 1.322-1.46 1.407-.463.341-.973.703-1.529 1.087-.556.383-1.112.703-1.668.959-.556.256-1.413.575-2.572.959a83.5 83.5 0 0 1-3.545 1.087 72.2 72.2 0 0 1-3.475.895c-1.112.256-1.946.426-2.502.511-1.112.17-1.854.043-2.224-.383-.371-.426-.464-1.151-.278-2.174.092-.511.278-1.279.556-2.302.278-1.023.602-2.067.973-3.132l1.042-3.005c.325-.938.58-1.577.765-1.918a10.157 10.157 0 0 1 2.224-2.941z"/></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M115.625 127.937H.063V12.375h57.781v12.374H12.438v90.813h90.813V70.156h12.374z"/><path d="M116.426 2.821l8.753 8.753-56.734 56.734-8.753-8.745z"/><path d="M127.893 37.982h-12.375V12.375H88.706V0h39.187z"/></svg>

After

Width:  |  Height:  |  Size: 285 B

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.002 9.2c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-5.043-3.58-9.132-7.997-9.132S.002 4.157.002 9.2zM31.997.066h95.981V18.33H31.997V.066zm0 45.669c0 5.044 3.58 9.132 7.998 9.132 4.417 0 7.997-4.088 7.997-9.132 0-3.263-1.524-6.278-3.998-7.91-2.475-1.63-5.524-1.63-7.998 0-2.475 1.632-4 4.647-4 7.91zM63.992 36.6h63.986v18.265H63.992V36.6zm-31.995 82.2c0 5.043 3.58 9.132 7.998 9.132 4.417 0 7.997-4.089 7.997-9.132 0-5.044-3.58-9.133-7.997-9.133s-7.998 4.089-7.998 9.133zm31.995-9.131h63.986v18.265H63.992V109.67zm0-27.404c0 5.044 3.58 9.133 7.998 9.133 4.417 0 7.997-4.089 7.997-9.133 0-3.263-1.524-6.277-3.998-7.909-2.475-1.631-5.524-1.631-7.998 0-2.475 1.632-4 4.646-4 7.91zm31.995-9.13h31.991V91.4H95.987V73.135z"/></svg>

After

Width:  |  Height:  |  Size: 821 B

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M108.8 44.322H89.6v-5.36c0-9.04-3.308-24.163-25.6-24.163-23.145 0-25.6 16.881-25.6 24.162v5.361H19.2v-5.36C19.2 15.281 36.798 0 64 0c27.202 0 44.8 15.281 44.8 38.961v5.361zm-32 39.356c0-5.44-5.763-9.832-12.8-9.832-7.037 0-12.8 4.392-12.8 9.832 0 3.682 2.567 6.808 6.407 8.477v11.205c0 2.718 2.875 4.962 6.4 4.962 3.524 0 6.4-2.244 6.4-4.962V92.155c3.833-1.669 6.393-4.795 6.393-8.477zM128 64v49.201c0 8.158-8.645 14.799-19.2 14.799H19.2C8.651 128 0 121.359 0 113.201V64c0-8.153 8.645-14.799 19.2-14.799h89.6c10.555 0 19.2 6.646 19.2 14.799z"/></svg>

After

Width:  |  Height:  |  Size: 623 B

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/><path d="M.006.064h127.988v31.104H.006V.064zm0 38.016h38.396v41.472H.006V38.08zm0 48.384h38.396v41.472H.006V86.464zM44.802 38.08h38.396v41.472H44.802V38.08zm0 48.384h38.396v41.472H44.802V86.464zM89.598 38.08h38.396v41.472H89.598zm0 48.384h38.396v41.472H89.598z"/></svg>

After

Width:  |  Height:  |  Size: 597 B

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -0,0 +1 @@
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>

After

Width:  |  Height:  |  Size: 440 B

@ -0,0 +1,22 @@
# replace default config
# multipass: true
# full: true
plugins:
# - name
#
# or:
# - name: false
# - name: true
#
# or:
# - name:
# param1: 1
# param2: 2
- removeAttrs:
attrs:
- 'fill'
- 'fill-rule'

@ -0,0 +1,40 @@
<template>
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<router-view :key="key" />
</transition>
</section>
</template>
<script>
export default {
name: "AppMain",
computed: {
key() {
return this.$route.path;
},
},
};
</script>
<style scoped>
.app-main {
/*50 = navbar */
min-height: calc(100vh - 50px);
width: 100%;
position: relative;
overflow: hidden;
}
.fixed-header + .app-main {
padding-top: 50px;
}
</style>
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 15px;
}
}
</style>

@ -0,0 +1,261 @@
<template>
<div class="navbar">
<div class="left_menu">
<img
src="../../static/home_logo.png"
style="height: 30px; width: 100px; margin-right: 60px"
mode="scaleToFill"
/>
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
@select="handleSelect"
active-text-color="#EC7C00"
>
<el-menu-item
index="1"
@click="
$router.push({
path: '/',
query: 111,
})
"
>首页</el-menu-item
>
<el-menu-item
index="2"
@click="
$router.push({
path: '/trend/index',
})
"
>趋势板块</el-menu-item
>
<el-menu-item
index="3"
@click="
$router.push({
path: '/private/index',
})
"
>个股详情</el-menu-item
>
<el-menu-item
index="4"
@click="
$router.push({
path: '/trade/index',
query: 111,
})
"
>交易记录</el-menu-item
>
<el-menu-item
index="5"
@click="
$router.push({
path: '/statistics/index',
query: 111,
})
"
>交易统计</el-menu-item
>
<el-menu-item
index="6"
@click="
$router.push({
path: '/collection/index',
query: 111,
})
"
>我的收藏</el-menu-item
>
<el-menu-item
index="8"
@click="
$router.push({
path: '/set/index',
query: 111,
})
"
>设置</el-menu-item
>
</el-menu>
</div>
<div class="right-menu">
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img src="../../static/moren_avatar.png" class="user-avatar" />
<div style="margin-left: 20px">
<div class="name">用户</div>
<div class="deadline">{{ data.phone }}</div>
</div>
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown" class="user-dropdown">
<el-dropdown-item divided @click.native="logout">
<span style="display: block">退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
import Hamburger from "@/components/Hamburger";
import { removeToken } from "@/utils/auth";
import { resetRouter } from "@/router";
import Cookies from "js-cookie";
export default {
components: {
Hamburger,
},
data() {
return {
activeIndex: "1",
data: {
avatar: "",
phone: "",
},
};
},
created() {
let index = Cookies.get("NavIndex");
this.data.phone = Cookies.get("gupiao_phone");
if (this.data.phone) {
var reg = /^(\d{3})\d{4}(\d{4})$/;
this.data.phone = this.data.phone.replace(reg, "$1****$2");
}
index ? (this.activeIndex = index) : (this.activeIndex = "1");
console.log("nav");
},
computed: {
...mapState(["count"]),
},
watch: {
// watch data vuex
count() {
this.activeIndex = this.$store.state.count;
},
},
methods: {
handleSelect(key) {
console.log(key);
Cookies.set("NavIndex", key);
},
submit() {
console.log(111);
},
async logout() {
// await this.$store.dispatch("user/logout");
Cookies.remove("NavIndex");
removeToken(); // must remove token first
resetRouter();
this.$store.commit("RESET_STATE");
this.$router.push(`/login`);
},
},
};
</script>
<style lang="scss" scoped>
.navbar {
padding: 0 20px;
height: 61px;
overflow: hidden;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
.breadcrumb-container {
float: left;
}
.right-menu {
float: right;
height: 100%;
// line-height: 50px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
}
.avatar-container {
margin-right: 30px;
.avatar-wrapper {
font-size: 24rpx;
margin-top: 5px;
position: relative;
display: flex;
align-items: center;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 40px;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
}
.left_menu {
float: left;
display: flex;
align-items: center;
}
.el-dropdown-menu__item--divided {
border: 0;
margin: 0;
}
.deadline {
font-size: 12px;
margin-top: 5px;
}
.name {
font-size: 16px;
font-weight: bold;
}
</style>

@ -0,0 +1,3 @@
export { default as Navbar } from "./Navbar";
// export { default as Sidebar } from './Sidebar'
export { default as AppMain } from "./AppMain";

@ -0,0 +1,97 @@
<template>
<div class="app-wrapper">
<!-- <div
v-if="device === 'mobile' && sidebar.opened"
class="drawer-bg"
@click="handleClickOutside"
/> -->
<!-- <sidebar class="sidebar-container" /> -->
<div class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar />
</div>
<app-main />
</div>
</div>
</template>
<script>
import { Navbar, AppMain } from "./components";
import ResizeMixin from "./mixin/ResizeHandler";
export default {
name: "Layout",
components: {
Navbar,
AppMain,
},
mixins: [ResizeMixin],
computed: {
device() {
return this.$store.state.app.device;
},
fixedHeader() {
return this.$store.state.settings.fixedHeader;
},
// classObj() {
// return {
// hideSidebar: !this.sidebar.opened,
// openSidebar: this.sidebar.opened,
// withoutAnimation: this.sidebar.withoutAnimation,
// mobile: this.device === "mobile",
// };
// },
},
methods: {
// handleClickOutside() {
// this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
// },
},
};
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
&.mobile.openSidebar {
position: fixed;
top: 0;
}
}
.drawer-bg {
background: #000;
opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
}
.fixed-header {
position: fixed;
top: 0;
right: 0;
z-index: 9;
// width: calc(100% - #{$sideBarWidth});
width: 100%;
transition: width 0.28s;
}
.hideSidebar .fixed-header {
width: 100%;
}
.mobile .fixed-header {
width: 100%;
}
.main-container {
background-color: #f0f2f5;
}
</style>

@ -0,0 +1,45 @@
import store from "@/store";
const { body } = document;
const WIDTH = 992; // refer to Bootstrap's responsive design
export default {
watch: {
$route(route) {
// if (this.device === 'mobile' && this.sidebar.opened) {
// store.dispatch('app/closeSideBar', { withoutAnimation: false })
// }
},
},
beforeMount() {
window.addEventListener("resize", this.$_resizeHandler);
},
beforeDestroy() {
window.removeEventListener("resize", this.$_resizeHandler);
},
mounted() {
const isMobile = this.$_isMobile();
if (isMobile) {
// store.dispatch("app/toggleDevice", "mobile");
// store.dispatch("app/closeSideBar", { withoutAnimation: true });
}
},
methods: {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_isMobile() {
const rect = body.getBoundingClientRect();
return rect.width - 1 < WIDTH;
},
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.$_isMobile();
// store.dispatch("app/toggleDevice", isMobile ? "mobile" : "desktop");
if (isMobile) {
// store.dispatch("app/closeSideBar", { withoutAnimation: true });
}
}
},
},
};

@ -0,0 +1,48 @@
import Vue from "vue";
import "normalize.css/normalize.css"; // A modern alternative to CSS resets
import ElementUI from "element-ui";
import "element-ui/lib/theme-chalk/index.css";
// require("./mock.js");
import locale from "element-ui/lib/locale/lang/en"; // lang i18n
import "@/styles/index.scss"; // global css
import App from "./App";
import store from "./store";
import router from "./router";
import VueCropper from "vue-cropper";
import Print from "vue-print-nb";
import "@/icons"; // icon
import "@/permission"; // permission control
import * as echarts from "echarts";
/**
* If you don't want to use mock-server
* you want to use MockJs for mock api
* you can execute: mockXHR()
*
* Currently MockJs will be used in the production environment,
* please remove it before going online ! ! !
*/
// mock数据
// if (process.env.NODE_ENV === 'production') {
// const { mockXHR } = require('../mock')
// mockXHR()
// }
// set ElementUI lang to EN
Vue.use(ElementUI, VueCropper);
Vue.use(Print);
// 如果想要中文版 element-ui按如下方式声明
// Vue.use(ElementUI)
Vue.config.productionTip = false;
Vue.prototype.$echarts = echarts;
new Vue({
el: "#app",
router,
store,
render: (h) => h(App),
});

@ -0,0 +1,169 @@
//添加一个mock规则
const Mock = require('mockjs')
//获取mock.random对象
const Random = Mock.Random
// kline模拟数据
const getMinute_k = function () {
let data = [];
for (let i = 0; i < 100; i++) {
let newArticleObject = {
id: i,
close: Random.natural(2800, 2888),
high: Random.natural(2800, 2888),
low: Random.natural(2800, 2888),
ma5: Random.natural(2800, 2888),
ma10: Random.natural(2800, 2888),
ma20: Random.natural(2800, 2888),
ma30: Random.natural(2800, 2888),
open: Random.natural(2800, 2888),
price: Random.natural(2800, 2888),
price_equal: Random.natural(1000, 2000),
time: Random.date(),
volume: Random.natural(10000, 20000),
yesterday_close: Random.natural(2800, 2888),
}
data.push(newArticleObject)
}
return {
data: data
}
}
//home默认数据
const getMarket = function () {
let data = {
buy_chang_price: Random.natural(0, 1),
buy_five_amount: false,
buy_five_price: "0",
buy_four_amount: false,
buy_four_price: "0",
buy_one_amount: false,
buy_one_price: "0",
buy_three_amount: false,
buy_three_price: "0",
buy_two_amount: false,
buy_two_price: "0",
code: "000001",
currency: 0,
current_price: Random.natural(2000, 3000),
debt_sign: 255,
exchange_code: "1",
highest: Random.natural(2000, 3000),
info: "",
lowest: Random.natural(2000, 3000),
mini_trans: 100,
name: "上证指数",
national_debt: "0.00",
open_price: Random.natural(2000, 3000),
price_range: Random.natural(2000, 3000),
price_rate: Random.natural(2000, 3000),
sell_chang_price: "0.01",
sell_five_amount: false,
sell_five_price: "0",
sell_four_amount: false,
sell_four_price: "0",
sell_one_amount: false,
sell_one_price: "0",
sell_three_amount: false,
sell_three_price: "0",
sell_two_amount: false,
sell_two_price: "0",
sub: 0,
time: Random.date(),
turnover: Random.natural(20000000, 30000000),
type: 1,
volume: Random.natural(2000000, 3000000),
yesterday_price: Random.natural(2000, 3000),
}
return {
data: data
}
}
//home 日k
const getDay_k = function () {
let data = [];
for (let i = 0; i < 100; i++) {
let newArticleObject = {
id: i,
close: Random.natural(2647, 3100),
high: Random.natural(2647, 3100),
low: Random.natural(2647, 3100),
ma5: Random.natural(2647, 3100),
ma10: Random.natural(2647, 3100),
ma20: Random.natural(2647, 3100),
ma30: Random.natural(3000, 4000),
open: Random.natural(2647, 3000),
price_equal: Random.natural(1000, 2000),
time: Random.date(),
volume: Random.natural(2000000, 3000000),
yesterday_close: Random.natural(2647, 3100),
}
data.push(newArticleObject)
}
return {
data: data
}
}
//home 周k
const getWeek_k = function () {
let data = [];
for (let i = 0; i < 100; i++) {
let newArticleObject = {
id: i,
close: Random.natural(2647, 3100),
high: Random.natural(2647, 3100),
low: Random.natural(2647, 3100),
ma5: Random.natural(2647, 3100),
ma10: Random.natural(2647, 3100),
ma20: Random.natural(2647, 3100),
ma30: Random.natural(3000, 4000),
open: Random.natural(2647, 3000),
price_equal: Random.natural(1000, 2000),
time: Random.date(),
volume: Random.natural(2000000, 3000000),
yesterday_close: Random.natural(2647, 3100),
}
data.push(newArticleObject)
}
return {
data: data
}
}
//home 月k
const getMonth_k = function () {
let data = [];
for (let i = 0; i < 100; i++) {
let newArticleObject = {
id: i,
close: Random.natural(2647, 3100),
high: Random.natural(2647, 3100),
low: Random.natural(2647, 3100),
ma5: Random.natural(2647, 3100),
ma10: Random.natural(2647, 3100),
ma20: Random.natural(2647, 3100),
ma30: Random.natural(3000, 4000),
open: Random.natural(2647, 3000),
price_equal: Random.natural(1000, 2000),
time: Random.date(),
volume: Random.natural(2000000, 3000000),
yesterday_close: Random.natural(2647, 3100),
}
data.push(newArticleObject)
}
return {
data: data
}
}
Mock.mock('/index/minute_k', 'post', getMinute_k); // kline模拟数据
Mock.mock('/index/market', 'post', getMarket); //home默认数据
Mock.mock('/index/day_k', 'get', getDay_k); //home日k
Mock.mock('/index/week_k', 'get', getWeek_k); //home周k
Mock.mock('/index/month_k', 'get', getMonth_k); //home月k

@ -0,0 +1,72 @@
import router from "./router";
import store from "./store";
import { Message } from "element-ui";
import NProgress from "nprogress"; // progress bar
import "nprogress/nprogress.css"; // progress bar style
import { getToken } from "@/utils/auth"; // get token from cookie
import getPageTitle from "@/utils/get-page-title";
NProgress.configure({ showSpinner: false }); // NProgress Configuration
const whiteList = ["/login"]; // no redirect whitelist
router.beforeEach(async (to, from, next) => {
// start progress bar
NProgress.start();
// set page title
document.title = "";
// determine whether the user has logged in
const hasToken = getToken();
// const hasToken = "";
// console.log(11);
// console.log(hasToken);
if (hasToken) {
console.log(to.path);
if (to.path === "/login") {
// if is logged in, redirect to the home page
console.log(111);
next({ path: "/" });
NProgress.done();
} else {
console.log(222);
const hasGetUserInfo = store.getters.name;
console.log(store.getters.name);
if (hasGetUserInfo) {
console.log(333);
next();
} else {
try {
console.log(444);
// get user info
// await store.dispatch("user/getInfo");
next();
} catch (error) {
console.log(555);
// remove token and go to login page to re-login
await store.dispatch("user/resetToken");
Message.error(error || "Has Error");
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next();
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
});
router.afterEach(() => {
// finish progress bar
NProgress.done();
});

@ -0,0 +1,169 @@
import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);
/* Layout */
import Layout from "@/layout";
/**
* Note: sub-menu only appear when route children.length >= 1
* Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
*
* hidden: true if set true, item will not show in the sidebar(default is false)
* alwaysShow: true if set true, will always show the root menu
* if not set alwaysShow, when item has more than one children route,
* it will becomes nested mode, otherwise not show the root menu
* redirect: noRedirect if set noRedirect will no redirect in the breadcrumb
* name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : {
roles: ['admin','editor'] control the page roles (you can set multiple roles)
title: 'title' the name show in sidebar and breadcrumb (recommend set)
icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
breadcrumb: false if set false, the item will hidden in breadcrumb(default is true)
activeMenu: '/example/list' if set path, the sidebar will highlight the path you set
}
*/
/**
* constantRoutes
* a base page that does not have permission requirements
* all roles can be accessed
*/
export const constantRoutes = [
{
path: "/login",
component: () => import("@/views/login/index"),
meta: {
title: "登录",
},
hidden: true,
},
{
path: "/404",
component: () => import("@/views/404"),
hidden: true,
},
{
path: "/",
component: Layout,
redirect: "/home",
// meta: {
// title: "首页",
// },
children: [
{
path: "home",
name: "Home",
component: () => import("@/views/home/index"),
},
],
},
{
path: "/private",
component: Layout,
children: [
{
path: "index",
name: "private",
component: () => import("@/views/private/index"),
meta: { title: "Form", icon: "form" },
},
],
},
{
path: "/trend",
component: Layout,
children: [
{
path: "index",
name: "Trend",
component: () => import("@/views/trend/index"),
// meta: { title: "Form", icon: "form" },
},
],
},
{
path: "/trade",
component: Layout,
children: [
{
path: "index",
name: "Trade",
component: () => import("@/views/trade/index"),
// meta: { title: "Form", icon: "form" },
},
],
},
{
path: "/collection",
component: Layout,
children: [
{
path: "index",
name: "Collection",
component: () => import("@/views/collection/index"),
// meta: { title: "Form", icon: "form" },
},
],
},
{
path: "/set",
component: Layout,
children: [
{
path: "index",
name: "Set",
component: () => import("@/views/set/index"),
// meta: { title: "Form", icon: "form" },
},
{
path: "editUser",
name: "editUser",
component: () => import("@/views/set/editUser"),
// meta: { title: "Form", icon: "form" },
},
],
},
{
path: "/statistics",
component: Layout,
children: [
{
path: "index",
name: "Statistics",
component: () => import("@/views/statistics/index"),
},
],
},
// 404 page must be placed at the end !!!
{ path: "*", redirect: "/404", hidden: true },
];
const createRouter = () =>
new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes,
});
const router = createRouter();
// 这个方法是为了每个页面添加属于自己的title
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter();
router.matcher = newRouter.matcher; // reset router
}
// router.beforeEach((to, from, next) => {
// if (to.meta.title) {
// document.title = to.meta.title;
// }
// next();
// });
export default router;

@ -0,0 +1,15 @@
module.exports = {
title: "摸金风向标",
/**
* @type {boolean} true | false
* @description Whether fix the header
*/
fixedHeader: false,
/**
* @type {boolean} true | false
* @description Whether show the logo in sidebar
*/
sidebarLogo: false,
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

@ -0,0 +1,8 @@
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name
}
export default getters

@ -0,0 +1,28 @@
import Vue from "vue";
import Vuex from "vuex";
import getters from "./getters";
import app from "./modules/app";
import settings from "./modules/settings";
import user from "./modules/user";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
app,
settings,
user,
},
getters,
state: {
count: null,
},
mutations: {
// 获取navindex
setCount(state, value) {
state.count = value;
},
},
});
export default store;

@ -0,0 +1,50 @@
// import Cookies from "js-cookie";
const state = {
// sidebar: {
// opened: Cookies.get("sidebarStatus")
// ? !!+Cookies.get("sidebarStatus")
// : true,
// withoutAnimation: false,
// },
// device: "desktop",
};
const mutations = {
// TOGGLE_SIDEBAR: (state) => {
// state.sidebar.opened = !state.sidebar.opened;
// state.sidebar.withoutAnimation = false;
// if (state.sidebar.opened) {
// Cookies.set("sidebarStatus", 1);
// } else {
// Cookies.set("sidebarStatus", 0);
// }
// },
// CLOSE_SIDEBAR: (state, withoutAnimation) => {
// Cookies.set("sidebarStatus", 0);
// state.sidebar.opened = false;
// state.sidebar.withoutAnimation = withoutAnimation;
// },
// TOGGLE_DEVICE: (state, device) => {
// state.device = device;
// },
};
const actions = {
// toggleSideBar({ commit }) {
// commit("TOGGLE_SIDEBAR");
// },
// closeSideBar({ commit }, { withoutAnimation }) {
// commit("CLOSE_SIDEBAR", withoutAnimation);
// },
// toggleDevice({ commit }, device) {
// commit("TOGGLE_DEVICE", device);
// },
};
export default {
namespaced: true,
state,
mutations,
// actions,
};

@ -0,0 +1,32 @@
import defaultSettings from '@/settings'
const { showSettings, fixedHeader, sidebarLogo } = defaultSettings
const state = {
showSettings: showSettings,
fixedHeader: fixedHeader,
sidebarLogo: sidebarLogo
}
const mutations = {
CHANGE_SETTING: (state, { key, value }) => {
// eslint-disable-next-line no-prototype-builtins
if (state.hasOwnProperty(key)) {
state[key] = value
}
}
}
const actions = {
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

@ -0,0 +1,106 @@
import { login, logout, getInfo } from "@/api/user";
import { getToken, setToken, removeToken } from "@/utils/auth";
import { resetRouter } from "@/router";
import Cookies from "js-cookie";
const getDefaultState = () => {
return {
token: getToken(),
name: "",
avatar: "",
};
};
const state = getDefaultState();
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState());
},
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_NAME: (state, name) => {
state.name = name;
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar;
},
};
const actions = {
// user login
login({ commit }, userInfo) {
const { phone, smsCode } = userInfo;
// console.log(generateSign({ phone: phone, password: password }));
return new Promise((resolve, reject) => {
login({ phone: phone, createWay: 1 })
.then((response) => {
console.log(response.data);
const data = response.data;
console.log(data);
Cookies.set("userInfo", data);
commit("SET_TOKEN", data);
setToken(data);
resolve();
})
.catch((error) => {
reject(error);
});
});
},
// get user info
// getInfo({ commit, state }) {
// return new Promise((resolve, reject) => {
// getInfo(state.token)
// .then((response) => {
// const { data } = response;
// if (!data) {
// return reject("Verification failed, please Login again.");
// }
// const { name, avatar } = data;
// commit("SET_NAME", name);
// commit("SET_AVATAR", avatar);
// resolve(data);
// })
// .catch((error) => {
// reject(error);
// });
// });
// },
// user logout
logout({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token)
.then(() => {
removeToken(); // must remove token first
resetRouter();
commit("RESET_STATE");
resolve();
})
.catch((error) => {
reject(error);
});
});
},
// remove token
resetToken({ commit }) {
return new Promise((resolve) => {
removeToken(); // must remove token first
commit("RESET_STATE");
resolve();
});
},
};
export default {
namespaced: true,
state,
mutations,
actions,
};

@ -0,0 +1,49 @@
// cover some element-ui styles
.el-breadcrumb__inner,
.el-breadcrumb__inner a {
font-weight: 400 !important;
}
.el-upload {
input[type="file"] {
display: none !important;
}
}
.el-upload__input {
display: none;
}
// to fixed https://github.com/ElemeFE/element/issues/2461
.el-dialog {
transform: none;
left: 0;
position: relative;
margin: 0 auto;
}
// refine element ui upload
.upload-container {
.el-upload {
width: 100%;
.el-upload-dragger {
width: 100%;
height: 200px;
}
}
}
// dropdown
.el-dropdown-menu {
a {
display: block
}
}
// to fix el-date-picker css style
.el-range-separator {
box-sizing: content-box;
}

@ -0,0 +1,63 @@
@import "./variables.scss";
@import "./mixin.scss";
@import "./transition.scss";
@import "./element-ui.scss";
@import "./sidebar.scss";
body {
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, Arial, sans-serif;
}
label {
font-weight: 700;
}
html {
height: 100%;
box-sizing: border-box;
}
#app {
height: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
div:focus {
outline: none;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
// main-container global css

@ -0,0 +1,28 @@
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}

@ -0,0 +1,227 @@
#app {
.main-container {
min-height: 100%;
transition: margin-left 0.28s;
position: relative;
}
.sidebar-container {
transition: width 0.28s;
width: $sideBarWidth !important;
background-color: $menuBg;
height: 100%;
position: fixed;
font-size: 0px;
top: 0;
bottom: 0;
left: 0;
z-index: 1001;
overflow: hidden;
// reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out,
0s padding-right ease-in-out;
}
.scrollbar-wrapper {
overflow-x: hidden !important;
}
.el-scrollbar__bar.is-vertical {
right: 0px;
}
.el-scrollbar {
height: 100%;
}
&.has-logo {
.el-scrollbar {
height: calc(100% - 50px);
}
}
.is-horizontal {
display: none;
}
a {
display: inline-block;
width: 100%;
overflow: hidden;
}
.svg-icon {
margin-right: 16px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
.el-menu {
border: none;
height: 100%;
width: 100% !important;
}
// menu hover
.submenu-title-noDropdown,
.el-submenu__title {
&:hover {
background-color: $menuHover !important;
}
}
.is-active > .el-submenu__title {
color: $subMenuActiveText !important;
}
& .nest-menu .el-submenu > .el-submenu__title,
& .el-submenu .el-menu-item {
min-width: $sideBarWidth !important;
background-color: $subMenuBg !important;
&:hover {
background-color: $subMenuHover !important;
}
}
}
.hideSidebar {
.sidebar-container {
width: 54px !important;
pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$sideBarWidth, 0, 0);
}
.main-container {
margin-left: 0px;
}
.submenu-title-noDropdown {
padding: 0 !important;
position: relative;
.el-tooltip {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-left: 19px;
}
}
}
.el-submenu {
overflow: hidden;
& > .el-submenu__title {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-left: 19px;
}
.el-submenu__icon-arrow {
display: none;
}
}
}
.el-menu--collapse {
.el-submenu {
& > .el-submenu__title {
& > span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
}
.el-menu--collapse .el-menu .el-submenu {
min-width: $sideBarWidth !important;
}
// mobile responsive
.mobile {
.main-container {
margin-left: 0px;
}
.sidebar-container {
transition: transform 0.28s;
width: $sideBarWidth !important;
}
&.hideSidebar {
.sidebar-container {
pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$sideBarWidth, 0, 0);
}
}
}
.withoutAnimation {
.main-container,
.sidebar-container {
transition: none;
}
}
}
// when menu collapsed
.el-menu--vertical {
& > .el-menu {
.svg-icon {
margin-right: 16px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
}
.nest-menu .el-submenu > .el-submenu__title,
.el-menu-item {
&:hover {
// you can use $subMenuHover
background-color: $menuHover !important;
}
}
// the scroll bar appears when the subMenu is too long
> .el-menu--popup {
max-height: 100vh;
overflow-y: auto;
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}
}

@ -0,0 +1,48 @@
// global transition css
/* fade */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.28s;
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}
/* fade-transform */
.fade-transform-leave-active,
.fade-transform-enter-active {
transition: all .5s;
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* breadcrumb transition */
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all .5s;
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all .5s;
}
.breadcrumb-leave-active {
position: absolute;
}

@ -0,0 +1,25 @@
// sidebar
$menuText:#bfcbd9;
$menuActiveText:#409EFF;
$subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
$menuBg:#304156;
$menuHover:#263445;
$subMenuBg:#1f2d3d;
$subMenuHover:#001528;
$sideBarWidth: 210px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
menuText: $menuText;
menuActiveText: $menuActiveText;
subMenuActiveText: $subMenuActiveText;
menuBg: $menuBg;
menuHover: $menuHover;
subMenuBg: $subMenuBg;
subMenuHover: $subMenuHover;
sideBarWidth: $sideBarWidth;
}

@ -0,0 +1,15 @@
import Cookies from 'js-cookie'
const TokenKey = 'vue_admin_template_token'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}

@ -0,0 +1,10 @@
import defaultSettings from "@/settings";
const title = defaultSettings.title || "摸金风向标";
export default function getPageTitle(pageTitle) {
if (pageTitle) {
return `${pageTitle} - ${title}`;
}
return `${title}`;
}

@ -0,0 +1,117 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* Parse the time to string
* @param {(Object|string|number)} time
* @param {string} cFormat
* @returns {string | null}
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0 || !time) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string')) {
if ((/^[0-9]+$/.test(time))) {
// support "1548221490638"
time = parseInt(time)
} else {
// support safari
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
time = time.replace(new RegExp(/-/gm), '/')
}
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
return value.toString().padStart(2, '0')
})
return time_str
}
/**
* @param {number} time
* @param {string} option
* @returns {string}
*/
export function formatTime(time, option) {
if (('' + time).length === 10) {
time = parseInt(time) * 1000
} else {
time = +time
}
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
// less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time, option)
} else {
return (
d.getMonth() +
1 +
'月' +
d.getDate() +
'日' +
d.getHours() +
'时' +
d.getMinutes() +
'分'
)
}
}
/**
* @param {string} url
* @returns {Object}
*/
export function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) {
return {}
}
const obj = {}
const searchArr = search.split('&')
searchArr.forEach(v => {
const index = v.indexOf('=')
if (index !== -1) {
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
obj[name] = val
}
})
return obj
}

@ -0,0 +1,96 @@
import axios from "axios";
import { Message, MessageBox } from "element-ui";
import store from "@/store";
import { getToken } from "@/utils/auth";
import { removeToken } from "@/utils/auth";
import { resetRouter } from "@/router";
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 30000,
});
service.interceptors.request.use(
(config) => {
const Authorization = config.headers["ssdmn-token"];
if (store.getters.token && !Authorization) {
// if (config.method === "POST") {
// config.header["Content-Type"] = "application/json; charset=UTF-8";
// }
config.headers["ssdmn-token"] = getToken();
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
/**
* 异常处理
* @param error
* @returns {Promise<never>}
*/
const errorHandler = (error) => {
console.log(error);
if (error.response) {
const response = error.response;
const status = Number(response.status) || 200;
const responseData = response.data;
const responseMsg = responseData.message || response["error_description"];
if (status === 403) {
Message({
message: "请求失败,暂无权限",
type: "error",
});
} else if (status === 401) {
/*
Message({
message: '令牌状态已过期,请点击重新登录',
type: 'error'
})*/
MessageBox.confirm("令牌状态已过期,请点击重新登录", "系统提示", {
confirmButtonText: "重新登录",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
store.dispatch("user/logout").then(() => {
// 刷新登录页面,避免多次弹框
window.location.reload();
});
})
.catch(() => {});
} else {
Message({
message: responseMsg,
type: "error",
});
}
}
return Promise.reject(error);
};
// HTTP response 拦截
service.interceptors.response.use((response) => {
console.log(response);
if (response.status === 200 && response.data.code === 0) {
Message({ type: "error", message: response.data.message });
return Promise.reject(response);
}
if (response.request.responseType === "blob") {
return response;
}
if (response.data.code === 200 || response.data.code === 204) {
return response.data;
} else if (response.data.code === 401) {
Message({ type: "error", message: "登录信息已过期,请重新登录" });
removeToken(); // must remove token first
resetRouter();
this.$store.commit("RESET_STATE");
this.$router.push(`/login`);
} else {
Message({ type: "error", message: response.data.msg });
}
}, errorHandler);
export default service;

@ -0,0 +1,20 @@
/**
* Created by PanJiaChen on 16/11/18.
*/
/**
* @param {string} path
* @returns {Boolean}
*/
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
/**
* @param {string} str
* @returns {Boolean}
*/
export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}

@ -0,0 +1,228 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/404_images/404.png" alt="404">
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">All rights reserved
<a style="color:#20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>
</div>
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">Please check that the URL you entered is correct, or click the button below to return to the homepage.</div>
<a href="" class="bullshit__return-home">Back to home</a>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Page404',
computed: {
message() {
return 'The webmaster said that you can not enter this page...'
}
}
}
</script>
<style lang="scss" scoped>
.wscn-http404-container{
transform: translate(-50%,-50%);
position: absolute;
top: 40%;
left: 50%;
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,215 @@
<template>
<div class="login">
<div class="login_left"></div>
<div class="login_right">
<div style="width: 100%">
<div class="login_text">登录</div>
<div class="login_ipt_box">
<div class="login_ipt_text">账号</div>
<el-input
v-model="loginForm.phone"
placeholder="请输入手机号"
class="ipt"
>
<!-- <template slot="append">
<span class="getcode" @click="getCode">{{ second }}</span>
</template> -->
</el-input>
<div class="login_ipt_text" style="margin-top: 40px">密码</div>
<el-input
v-model="loginForm.smsCode"
placeholder="请输入密码"
class="ipt"
show-password
>
</el-input>
</div>
<!-- <div class="login_type">
<div>微信登录</div>
<div>游客登录</div>
</div> -->
<button class="btn" @click="submit"></button>
</div>
</div>
</div>
</template>
<script>
import { sendSms } from "@/api/user";
import Cookies from "js-cookie";
export default {
name: "Login",
data() {
return {
loginForm: {
phone: "",
smsCode: "",
// createWay: 1,
},
second: "获取验证码",
flag: true,
timer: null, // ,setInterval
};
},
watch: {
$route: {
handler: function (route) {
this.redirect = route.query && route.query.redirect;
},
immediate: true,
},
},
created() {
document.title = "登录";
},
mounted() {
this.judgeCode();
},
methods: {
getCode() {
sendSms({
phone: this.loginForm.phone,
})
.then((res) => {})
.catch(() => {
// this.shareLoading = false;
});
let that = this;
if (that.flag) {
that.flag = false;
that.second = 60;
let interval = window.setInterval(function () {
that.setStorage(that.second);
if (that.second-- <= 0) {
that.second = "获取验证码";
that.flag = true;
window.clearInterval(interval);
}
}, 1000);
}
},
setStorage(parm) {
localStorage.setItem("dalay", parm);
localStorage.setItem("_time", new Date().getTime());
},
judgeCode() {
let that = this;
let localDelay = that.getStorage();
let secTime = parseInt((new Date().getTime() - localDelay.sec) / 1000);
console.log(localDelay);
if (secTime > localDelay.delay) {
that.flag = true;
console.log("已过期");
} else {
that.flag = false;
let _delay = localDelay.delay - secTime;
that.second = _delay;
that.timer = setInterval(function () {
if (_delay > 1) {
_delay--;
that.setStorage(_delay);
that.second = _delay;
that.flag = false;
} else {
// ,
that.flag = true;
window.clearInterval(that.timer);
}
if (that.second-- <= 0) {
that.second = "获取验证码";
that.flag = true;
window.clearInterval(interval);
}
}, 1000);
}
},
getStorage() {
let localDelay = {};
localDelay.delay = localStorage.getItem("dalay");
localDelay.sec = localStorage.getItem("_time");
return localDelay;
},
submit() {
if (this.loginForm.phone && this.loginForm.smsCode) {
this.loading = true;
Cookies.set("gupiao_phone", this.loginForm.phone);
this.$store
.dispatch("user/login", this.loginForm)
.then(() => {
console.log(1);
this.$router.push({ name: "Home" });
this.loading = false;
})
.catch((err) => {
console.log(err);
// this.$router.push({ path: "/" });
this.loading = false;
});
} else {
this.$message.error("请输入账号密码");
}
},
},
};
</script>
<style lang="scss" scoped>
.login {
display: flex;
}
.login_left {
width: 65%;
height: 100vh;
background-image: url(../../static/login_bgc.png);
background-size: 100% 100%;
background-repeat: no-repeat;
}
.login_right {
flex: 1;
padding: 0 62px;
background-color: #fff;
display: flex;
align-items: center;
.login {
&_text {
font-size: 40px;
color: #333;
}
}
.login_ipt_box {
margin-top: 60px;
.login_ipt_text {
font-size: 18px;
color: #999999;
margin-bottom: 12px;
}
.ipt {
width: 100%;
}
}
.login_type {
margin-top: 28px;
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
}
.btn {
margin-top: 70px;
width: 100%;
height: 50px;
background-color: #ec7c00;
border-radius: 8px;
color: white;
border: 0;
cursor: pointer;
}
}
::v-deep .el-input-group__append,
.el-input-group__prepend {
color: #ec7c00;
}
.getcode {
cursor: pointer;
}
</style>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,170 @@
<template>
<div class="app-container">
<div class="main">
<div class="main_title">编辑信息</div>
<div class="main_set">
<el-form :model="form" :rules="rules" ref="form" label-width="130px">
<el-form-item label="昵称" prop="name">
<el-input
v-model="form.name"
autocomplete="off"
placeholder="请输入昵称"
></el-input>
</el-form-item>
<el-form-item
label="店铺头像"
prop="Img"
:rules="[
{ required: true, message: '请上传头像', trigger: 'blur' },
]"
>
<div class="id_img">
<el-upload
class="avatar-uploader"
action="https://yyq.tt.demo.cq1080.com/store/v1/uploadGoods"
:show-file-list="false"
:headers="token"
:on-success="addavatar"
:on-change="beforeAvatarUpload"
>
<img v-if="form.avatar" :src="form.avatar" class="avatar1" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</el-form-item>
</el-form>
</div>
<div class="btn_box">
<el-button type="primary" @click="submitForm('form')"></el-button>
<el-button
type="info"
@click="
$router.push({
path: '/set/index',
})
"
>返回</el-button
>
</div>
</div>
</div>
</template>
<script>
import Cookies from "js-cookie";
export default {
data() {
return {
token: { Authorization: "" },
avatar: "",
form: {
//
name: "",
},
rules: {
name: [
{ required: true, message: "请输入昵称", trigger: "blur" },
{ min: 2, max: 5, message: "长度在 2 到 5 个字符", trigger: "blur" },
],
},
};
},
created() {
document.title = "编辑";
this.token.Authorization = Cookies.get("vue_admin_template_token");
},
methods: {
addavatar(res, file) {
console.log(res);
if (res.data.url) {
this.form.avatar = res.data.url;
}
},
beforeAvatarUpload(file) {
if (file.raw.type == "image/jpeg" || file.raw.type == "image/png") {
return;
} else {
this.$message.error("上传图片只能是 JPG/PNG 格式!");
}
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
// pceEditStoreInfo(this.form)
// .then((res) => {
// this.$message({
// message: "",
// type: "success",
// });
// })
// .catch(() => {
// // this.shareLoading = false;
// });
} else {
console.log("error submit!!");
return false;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.app-container {
height: calc(100vh - 61px);
padding: 20px;
width: 100%;
// background-color: white;
}
.main {
position: relative;
width: 100%;
height: 100%;
background-color: white;
border-radius: 10px;
padding: 20px;
overflow-y: auto;
}
.main_title {
font-size: 20px;
font-weight: bold;
}
.main_set {
margin-top: 40px;
width: 30%;
}
.avatar-uploader {
margin-right: 20px;
}
.avatar-uploader ::v-deep .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 120px;
height: 120px;
line-height: 120px;
text-align: center;
}
.id_img {
display: flex;
}
.avatar1 {
width: 120px;
height: 120px;
display: block;
}
// .btn_box {
// width: 30%;
// display: flex;
// align-items: center;
// justify-content: center;
// }
</style>

@ -0,0 +1,93 @@
<template>
<div class="app-container">
<div class="main">
<div class="center">
<div class="info_text">个人信息</div>
<div class="info">
<div>用户昵称</div>
<div style="margin-left: 40px">
<img
src="https://upload-bbs.mihoyo.com/upload/2021/03/21/184509926/b0eabac966513ab4dd30b6be6b1fe24b_7620507583700315542.jpg?x-oss-process=image/resize,s_600/quality,q_80/auto-orient,0/interlace,1/format,jpg"
style="width: 50px; height: 50px; border-radius: 50px"
alt=""
/>
</div>
</div>
<div class="info">
<div>用户头像</div>
<div style="margin-left: 40px">{{ data.name }}</div>
</div>
<div class="info">
<div>手机号</div>
<div style="margin-left: 60px">{{ data.name }}</div>
</div>
<div
class="btn"
@click="
$router.push({
path: 'editUser',
})
"
>
编辑
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
data: {
name: "张三",
},
};
},
created() {
document.title = "个人信息";
},
methods: {},
};
</script>
<style lang="scss" scoped>
.app-container {
padding: 20px;
width: 100%;
// background-color: white;
}
.main {
background-color: white;
width: 100%;
height: calc(100vh - 100px);
background-color: white;
overflow-y: auto;
}
.center {
padding: 36px 48px;
font-size: 20px;
}
.info_text {
font-size: 30px;
font-weight: bold;
}
.info {
margin-top: 40px;
display: flex;
align-items: center;
// justify-content: space-between;
}
.btn {
width: 100px;
height: 40px;
border: 1px solid #ec7c00;
margin-top: 100px;
font-size: 14px;
color: #ec7c00;
line-height: 40px;
text-align: center;
border-radius: 4px;
cursor: pointer;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,5 @@
module.exports = {
env: {
jest: true
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save