@ -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']
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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,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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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}`)
|
||||||
|
}
|
||||||
@ -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': {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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>
|
||||||
|
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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 96 KiB |
|
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)
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 497 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 944 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 285 B |
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 623 B |
|
After Width: | Height: | Size: 597 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
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,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,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,
|
||||||
|
};
|
||||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 491 B |
|
After Width: | Height: | Size: 532 B |
|
After Width: | Height: | Size: 519 B |
|
After Width: | Height: | Size: 448 B |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 421 B |
|
After Width: | Height: | Size: 501 B |
|
After Width: | Height: | Size: 677 B |
|
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>
|
||||||
@ -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>
|
||||||
@ -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>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
jest: true
|
||||||
|
}
|
||||||
|
}
|
||||||