├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── .travis.yml
├── LICENSE
├── README-zh.md
├── README.md
├── build
├── build.js
├── check-versions.js
├── logo.png
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.dev.conf.js
└── webpack.prod.conf.js
├── config
├── dev.env.js
├── index.js
└── prod.env.js
├── favicon.ico
├── index.html
├── package.json
├── src
├── App.vue
├── api
│ ├── account.js
│ ├── indicator.js
│ ├── login.js
│ ├── nodes.js
│ ├── order.js
│ ├── robot.js
│ ├── strategy.js
│ ├── symbol.js
│ ├── table.js
│ └── user.js
├── assets
│ └── 404_images
│ │ ├── 404.png
│ │ └── 404_cloud.png
├── components
│ ├── Breadcrumb
│ │ └── index.vue
│ ├── Hamburger
│ │ └── index.vue
│ ├── Pagination
│ │ └── index.vue
│ └── SvgIcon
│ │ └── index.vue
├── directive
│ ├── clipboard
│ │ ├── clipboard.js
│ │ └── index.js
│ ├── el-dragDialog
│ │ ├── drag.js
│ │ └── index.js
│ ├── el-table
│ │ ├── adaptive.js
│ │ └── index.js
│ ├── permission
│ │ ├── index.js
│ │ └── permission.js
│ ├── sticky.js
│ └── waves
│ │ ├── index.js
│ │ ├── waves.css
│ │ └── waves.js
├── icons
│ ├── index.js
│ ├── svg
│ │ ├── 404.svg
│ │ ├── bug.svg
│ │ ├── chart.svg
│ │ ├── clipboard.svg
│ │ ├── component.svg
│ │ ├── dashboard.svg
│ │ ├── documentation.svg
│ │ ├── drag.svg
│ │ ├── edit.svg
│ │ ├── email.svg
│ │ ├── example.svg
│ │ ├── excel.svg
│ │ ├── exit-fullscreen.svg
│ │ ├── eye-open.svg
│ │ ├── eye.svg
│ │ ├── form.svg
│ │ ├── fullscreen.svg
│ │ ├── guide 2.svg
│ │ ├── guide.svg
│ │ ├── icon.svg
│ │ ├── international.svg
│ │ ├── language.svg
│ │ ├── link.svg
│ │ ├── list.svg
│ │ ├── lock.svg
│ │ ├── message.svg
│ │ ├── money.svg
│ │ ├── nested.svg
│ │ ├── password.svg
│ │ ├── pdf.svg
│ │ ├── people.svg
│ │ ├── peoples.svg
│ │ ├── qq.svg
│ │ ├── search.svg
│ │ ├── shopping.svg
│ │ ├── size.svg
│ │ ├── star.svg
│ │ ├── tab.svg
│ │ ├── table.svg
│ │ ├── theme.svg
│ │ ├── tree-table.svg
│ │ ├── tree.svg
│ │ ├── user.svg
│ │ ├── wechat.svg
│ │ └── zip.svg
│ └── svgo.yml
├── main.js
├── permission.js
├── router
│ └── index.js
├── store
│ ├── getters.js
│ ├── index.js
│ └── modules
│ │ ├── app.js
│ │ └── user.js
├── styles
│ ├── btn.scss
│ ├── element-ui.scss
│ ├── element-variables.scss
│ ├── index.scss
│ ├── mixin.scss
│ ├── sidebar.scss
│ ├── transition.scss
│ └── variables.scss
├── utils
│ ├── auth.js
│ ├── clipboard.js
│ ├── date.js
│ ├── errorLog.js
│ ├── i18n.js
│ ├── index.js
│ ├── openWindow.js
│ ├── permission.js
│ ├── request.js
│ ├── scrollTo.js
│ └── validate.js
└── views
│ ├── 404.vue
│ ├── account
│ ├── email.vue
│ ├── index.vue
│ └── info.vue
│ ├── dashboard
│ └── index.vue
│ ├── layout
│ ├── Layout.vue
│ ├── components
│ │ ├── AppMain.vue
│ │ ├── Navbar.vue
│ │ ├── Sidebar
│ │ │ ├── Item.vue
│ │ │ ├── Link.vue
│ │ │ ├── SidebarItem.vue
│ │ │ └── index.vue
│ │ └── index.js
│ └── mixin
│ │ └── ResizeHandler.js
│ ├── login
│ └── index.vue
│ ├── order
│ ├── list.vue
│ └── profit.vue
│ ├── robot
│ ├── index.vue
│ ├── info.vue
│ └── list.vue
│ └── strategy
│ ├── index.vue
│ ├── indicator.vue
│ └── list.vue
└── static
└── .gitkeep
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", {
4 | "modules": false,
5 | "targets": {
6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
7 | }
8 | }],
9 | "stage-2"
10 | ],
11 | "plugins":["transform-vue-jsx", "transform-runtime"]
12 | }
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | insert_final_newline = false
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 | src/assets
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | parser: 'babel-eslint',
5 | sourceType: 'module'
6 | },
7 | env: {
8 | browser: true,
9 | node: true,
10 | es6: true,
11 | },
12 | extends: ['plugin:vue/recommended', 'eslint:recommended'],
13 |
14 | // add your custom rules here
15 | //it is base on https://github.com/vuejs/eslint-config-vue
16 | rules: {
17 | "vue/max-attributes-per-line": [2, {
18 | "singleline": 10,
19 | "multiline": {
20 | "max": 1,
21 | "allowFirstLine": false
22 | }
23 | }],
24 | "vue/name-property-casing": ["error", "PascalCase"],
25 | 'accessor-pairs': 2,
26 | 'arrow-spacing': [2, {
27 | 'before': true,
28 | 'after': true
29 | }],
30 | 'block-spacing': [2, 'always'],
31 | 'brace-style': [2, '1tbs', {
32 | 'allowSingleLine': true
33 | }],
34 | 'camelcase': [0, {
35 | 'properties': 'always'
36 | }],
37 | 'comma-dangle': [2, 'never'],
38 | 'comma-spacing': [2, {
39 | 'before': false,
40 | 'after': true
41 | }],
42 | 'comma-style': [2, 'last'],
43 | 'constructor-super': 2,
44 | 'curly': [2, 'multi-line'],
45 | 'dot-location': [2, 'property'],
46 | 'eol-last': 2,
47 | 'eqeqeq': [2, 'allow-null'],
48 | 'generator-star-spacing': [2, {
49 | 'before': true,
50 | 'after': true
51 | }],
52 | 'handle-callback-err': [2, '^(err|error)$'],
53 | 'indent': [2, 2, {
54 | 'SwitchCase': 1
55 | }],
56 | 'jsx-quotes': [2, 'prefer-single'],
57 | 'key-spacing': [2, {
58 | 'beforeColon': false,
59 | 'afterColon': true
60 | }],
61 | 'keyword-spacing': [2, {
62 | 'before': true,
63 | 'after': true
64 | }],
65 | 'new-cap': [2, {
66 | 'newIsCap': true,
67 | 'capIsNew': false
68 | }],
69 | 'new-parens': 2,
70 | 'no-array-constructor': 2,
71 | 'no-caller': 2,
72 | 'no-console': 'off',
73 | 'no-class-assign': 2,
74 | 'no-cond-assign': 2,
75 | 'no-const-assign': 2,
76 | 'no-control-regex': 2,
77 | 'no-delete-var': 2,
78 | 'no-dupe-args': 2,
79 | 'no-dupe-class-members': 2,
80 | 'no-dupe-keys': 2,
81 | 'no-duplicate-case': 2,
82 | 'no-empty-character-class': 2,
83 | 'no-empty-pattern': 2,
84 | 'no-eval': 2,
85 | 'no-ex-assign': 2,
86 | 'no-extend-native': 2,
87 | 'no-extra-bind': 2,
88 | 'no-extra-boolean-cast': 2,
89 | 'no-extra-parens': [2, 'functions'],
90 | 'no-fallthrough': 2,
91 | 'no-floating-decimal': 2,
92 | 'no-func-assign': 2,
93 | 'no-implied-eval': 2,
94 | 'no-inner-declarations': [2, 'functions'],
95 | 'no-invalid-regexp': 2,
96 | 'no-irregular-whitespace': 2,
97 | 'no-iterator': 2,
98 | 'no-label-var': 2,
99 | 'no-labels': [2, {
100 | 'allowLoop': false,
101 | 'allowSwitch': false
102 | }],
103 | 'no-lone-blocks': 2,
104 | 'no-mixed-spaces-and-tabs': 2,
105 | 'no-multi-spaces': 2,
106 | 'no-multi-str': 2,
107 | 'no-multiple-empty-lines': [2, {
108 | 'max': 1
109 | }],
110 | 'no-native-reassign': 2,
111 | 'no-negated-in-lhs': 2,
112 | 'no-new-object': 2,
113 | 'no-new-require': 2,
114 | 'no-new-symbol': 2,
115 | 'no-new-wrappers': 2,
116 | 'no-obj-calls': 2,
117 | 'no-octal': 2,
118 | 'no-octal-escape': 2,
119 | 'no-path-concat': 2,
120 | 'no-proto': 2,
121 | 'no-redeclare': 2,
122 | 'no-regex-spaces': 2,
123 | 'no-return-assign': [2, 'except-parens'],
124 | 'no-self-assign': 2,
125 | 'no-self-compare': 2,
126 | 'no-sequences': 2,
127 | 'no-shadow-restricted-names': 2,
128 | 'no-spaced-func': 2,
129 | 'no-sparse-arrays': 2,
130 | 'no-this-before-super': 2,
131 | 'no-throw-literal': 2,
132 | 'no-trailing-spaces': 2,
133 | 'no-undef': 2,
134 | 'no-undef-init': 2,
135 | 'no-unexpected-multiline': 2,
136 | 'no-unmodified-loop-condition': 2,
137 | 'no-unneeded-ternary': [2, {
138 | 'defaultAssignment': false
139 | }],
140 | 'no-unreachable': 2,
141 | 'no-unsafe-finally': 2,
142 | 'no-unused-vars': [2, {
143 | 'vars': 'all',
144 | 'args': 'none'
145 | }],
146 | 'no-useless-call': 2,
147 | 'no-useless-computed-key': 2,
148 | 'no-useless-constructor': 2,
149 | 'no-useless-escape': 0,
150 | 'no-whitespace-before-property': 2,
151 | 'no-with': 2,
152 | 'one-var': [2, {
153 | 'initialized': 'never'
154 | }],
155 | 'operator-linebreak': [2, 'after', {
156 | 'overrides': {
157 | '?': 'before',
158 | ':': 'before'
159 | }
160 | }],
161 | 'padded-blocks': [2, 'never'],
162 | 'quotes': [2, 'single', {
163 | 'avoidEscape': true,
164 | 'allowTemplateLiterals': true
165 | }],
166 | 'semi': [2, 'never'],
167 | 'semi-spacing': [2, {
168 | 'before': false,
169 | 'after': true
170 | }],
171 | 'space-before-blocks': [2, 'always'],
172 | 'space-before-function-paren': [2, 'never'],
173 | 'space-in-parens': [2, 'never'],
174 | 'space-infix-ops': 2,
175 | 'space-unary-ops': [2, {
176 | 'words': true,
177 | 'nonwords': false
178 | }],
179 | 'spaced-comment': [2, 'always', {
180 | 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
181 | }],
182 | 'template-curly-spacing': [2, 'never'],
183 | 'use-isnan': 2,
184 | 'valid-typeof': 2,
185 | 'wrap-iife': [2, 'any'],
186 | 'yield-star-spacing': [2, 'both'],
187 | 'yoda': [2, 'never'],
188 | 'prefer-const': 2,
189 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
190 | 'object-curly-spacing': [2, 'always', {
191 | objectsInObjects: false
192 | }],
193 | 'array-bracket-spacing': [2, 'never']
194 | }
195 | }
196 |
197 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | package-lock.json
8 |
9 | # Editor directories and files
10 | .idea
11 | .vscode
12 | *.suo
13 | *.ntvs*
14 | *.njsproj
15 | *.sln
16 | src/views/dashboard/index.vue
17 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | "postcss-import": {},
6 | "postcss-url": {},
7 | // to edit target browsers: use "browserslist" field in package.json
8 | "autoprefixer": {}
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: stable
3 | script: npm run test
4 | notifications:
5 | email: false
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-present PanJiaChen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-zh.md:
--------------------------------------------------------------------------------
1 | # vue-admin-template
2 |
3 | > 这是一个 极简的 vue admin 管理后台 它只包含了 Element UI & axios & iconfont & permission control & lint,这些搭建后台必要的东西。
4 |
5 | [线上地址](http://panjiachen.github.io/vue-admin-template)
6 |
7 | [国内访问](https://panjiachen.gitee.io/vue-admin-template)
8 |
9 | ## Extra
10 |
11 | 如果你想要根据用户角色来动态生成侧边栏和 router,你可以使用该分支[permission-control](https://github.com/PanJiaChen/vue-admin-template/tree/permission-control)
12 |
13 | 本项目基于`webpack4`开发,若还想使用`webpack3`开发,请使用该分支[webpack3](https://github.com/PanJiaChen/vue-admin-template/tree/webpack3)
14 |
15 | 如果你想使用基于 vue + typescript 的管理后台, 可以看看这个项目: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
16 |
17 | ## 相关项目
18 |
19 | [vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
20 |
21 | [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
22 |
23 | [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template)
24 |
25 | 写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
26 |
27 | - [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
28 | - [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
29 | - [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
30 | - [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板,专门针对本项目的文章,算作是一篇文档)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
31 | - [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
32 |
33 | ## Build Setup
34 |
35 | ```bash
36 | # Clone project
37 | git clone https://github.com/PanJiaChen/vue-admin-template.git
38 |
39 | # Install dependencies
40 | npm install
41 |
42 | # 建议不要用cnpm 安装有各种诡异的bug 可以通过如下操作解决npm速度慢的问题
43 | npm install --registry=https://registry.npm.taobao.org
44 |
45 | # Serve with hot reload at localhost:9528
46 | npm run dev
47 |
48 | # Build for production with minification
49 | npm run build
50 |
51 | # Build for production and view the bundle analyzer report
52 | npm run build --report
53 | ```
54 |
55 | ## Demo
56 |
57 | 
58 |
59 | ### Element-Ui 使用 cdn 教程
60 |
61 | 首先找到 `index.html` ([根目录下](https://github.com/PanJiaChen/vue-admin-template/blob/element-ui-cdn/index.html))
62 |
63 | 引入 Element 的 css 和 js ,并且引入 vue 。因为 Element-Ui 是依赖 vue 的,所以必须在它之前引入 vue 。
64 |
65 | 之后找到 [webpack.base.conf.js](https://github.com/PanJiaChen/vue-admin-template/blob/element-ui-cdn/build/webpack.base.conf.js) 加入 `externals` 让 webpack 不打包 vue 和 element
66 |
67 | ```
68 | externals: {
69 | vue: 'Vue',
70 | 'element-ui':'ELEMENT'
71 | }
72 | ```
73 |
74 | 之后还有一个小细节是如果你用了全局对象方式引入 vue,就不需要 手动 `Vue.use(Vuex)` ,它会自动挂载,具体见 [issue](https://github.com/vuejs/vuex/issues/731)
75 |
76 | 最终你可以使用 `npm run build --report` 查看效果
77 | 如图:
78 | 
79 |
80 | **[具体代码](https://github.com/PanJiaChen/vue-admin-template/commit/746aff560932704ae821f82f10b8b2a9681d5177)**
81 |
82 | **[对应分支](https://github.com/PanJiaChen/vue-admin-template/tree/element-ui-cdn)**
83 |
84 | ## Browsers support
85 |
86 | Modern browsers and Internet Explorer 10+.
87 |
88 | | [
](http://godban.github.io/browsers-support-badges/)IE / Edge | [
](http://godban.github.io/browsers-support-badges/)Firefox | [
](http://godban.github.io/browsers-support-badges/)Chrome | [
](http://godban.github.io/browsers-support-badges/)Safari |
89 | | --------- | --------- | --------- | --------- |
90 | | IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
91 |
92 | ## License
93 |
94 | [MIT](https://github.com/PanJiaChen/vue-admin-template/blob/master/LICENSE) license.
95 |
96 | Copyright (c) 2017-present PanJiaChen
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # quant-admin
2 |
3 |
4 | ## Build Setup
5 |
6 | ```bash
7 | # Clone project
8 | git clone https://github.com/tokenIsme/quant-admin.git
9 |
10 | # Install dependencies
11 | npm install
12 |
13 | # Serve with hot reload at localhost:9528
14 | npm run dev
15 |
16 | # Build for production with minification
17 | npm run build
18 |
19 | # Build for production and view the bundle analyzer report
20 | npm run build --report
21 |
22 | ```
23 | ## Donate
24 |
25 | 如果你觉得这个项目帮助到了你,你可以帮作者点个star表示鼓励 :tropical_drink:
26 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | require('./check-versions')()
3 |
4 | process.env.NODE_ENV = 'production'
5 |
6 | const ora = require('ora')
7 | const rm = require('rimraf')
8 | const path = require('path')
9 | const chalk = require('chalk')
10 | const webpack = require('webpack')
11 | const config = require('../config')
12 | const webpackConfig = require('./webpack.prod.conf')
13 |
14 | const spinner = ora('building for production...')
15 | spinner.start()
16 |
17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
18 | if (err) throw err
19 | webpack(webpackConfig, (err, stats) => {
20 | spinner.stop()
21 | if (err) throw err
22 | process.stdout.write(
23 | stats.toString({
24 | colors: true,
25 | modules: false,
26 | children: false,
27 | chunks: false,
28 | chunkModules: false
29 | }) + '\n\n'
30 | )
31 |
32 | if (stats.hasErrors()) {
33 | console.log(chalk.red(' Build failed with errors.\n'))
34 | process.exit(1)
35 | }
36 |
37 | console.log(chalk.cyan(' Build complete.\n'))
38 | console.log(
39 | chalk.yellow(
40 | ' Tip: built files are meant to be served over an HTTP server.\n' +
41 | " Opening index.html over file:// won't work.\n"
42 | )
43 | )
44 | })
45 | })
46 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const chalk = require('chalk')
3 | const semver = require('semver')
4 | const packageConfig = require('../package.json')
5 | const shell = require('shelljs')
6 |
7 | function exec(cmd) {
8 | return require('child_process')
9 | .execSync(cmd)
10 | .toString()
11 | .trim()
12 | }
13 |
14 | const versionRequirements = [
15 | {
16 | name: 'node',
17 | currentVersion: semver.clean(process.version),
18 | versionRequirement: packageConfig.engines.node
19 | }
20 | ]
21 |
22 | if (shell.which('npm')) {
23 | versionRequirements.push({
24 | name: 'npm',
25 | currentVersion: exec('npm --version'),
26 | versionRequirement: packageConfig.engines.npm
27 | })
28 | }
29 |
30 | module.exports = function() {
31 | const warnings = []
32 |
33 | for (let i = 0; i < versionRequirements.length; i++) {
34 | const mod = versionRequirements[i]
35 |
36 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
37 | warnings.push(
38 | mod.name +
39 | ': ' +
40 | chalk.red(mod.currentVersion) +
41 | ' should be ' +
42 | chalk.green(mod.versionRequirement)
43 | )
44 | }
45 | }
46 |
47 | if (warnings.length) {
48 | console.log('')
49 | console.log(
50 | chalk.yellow(
51 | 'To use this template, you must update following to modules:'
52 | )
53 | )
54 | console.log()
55 |
56 | for (let i = 0; i < warnings.length; i++) {
57 | const warning = warnings[i]
58 | console.log(' ' + warning)
59 | }
60 |
61 | console.log()
62 | process.exit(1)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/build/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndexOutOfBounds998/quant-admin/e894b3d33ee3abd2e9b2b946b7e1b5676bce16cb/build/logo.png
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const config = require('../config')
4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
5 | const packageConfig = require('../package.json')
6 |
7 | exports.assetsPath = function(_path) {
8 | const assetsSubDirectory =
9 | process.env.NODE_ENV === 'production'
10 | ? config.build.assetsSubDirectory
11 | : config.dev.assetsSubDirectory
12 |
13 | return path.posix.join(assetsSubDirectory, _path)
14 | }
15 |
16 | exports.cssLoaders = function(options) {
17 | options = options || {}
18 |
19 | const cssLoader = {
20 | loader: 'css-loader',
21 | options: {
22 | sourceMap: options.sourceMap
23 | }
24 | }
25 |
26 | const postcssLoader = {
27 | loader: 'postcss-loader',
28 | options: {
29 | sourceMap: options.sourceMap
30 | }
31 | }
32 |
33 | // generate loader string to be used with extract text plugin
34 | function generateLoaders(loader, loaderOptions) {
35 | const loaders = []
36 |
37 | // Extract CSS when that option is specified
38 | // (which is the case during production build)
39 | if (options.extract) {
40 | loaders.push(MiniCssExtractPlugin.loader)
41 | } else {
42 | loaders.push('vue-style-loader')
43 | }
44 |
45 | loaders.push(cssLoader)
46 |
47 | if (options.usePostCSS) {
48 | loaders.push(postcssLoader)
49 | }
50 |
51 | if (loader) {
52 | loaders.push({
53 | loader: loader + '-loader',
54 | options: Object.assign({}, loaderOptions, {
55 | sourceMap: options.sourceMap
56 | })
57 | })
58 | }
59 |
60 | return loaders
61 | }
62 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
63 | return {
64 | css: generateLoaders(),
65 | postcss: generateLoaders(),
66 | less: generateLoaders('less'),
67 | sass: generateLoaders('sass', {
68 | indentedSyntax: true
69 | }),
70 | scss: generateLoaders('sass'),
71 | stylus: generateLoaders('stylus'),
72 | styl: generateLoaders('stylus')
73 | }
74 | }
75 |
76 | // Generate loaders for standalone style files (outside of .vue)
77 | exports.styleLoaders = function(options) {
78 | const output = []
79 | const loaders = exports.cssLoaders(options)
80 |
81 | for (const extension in loaders) {
82 | const loader = loaders[extension]
83 | output.push({
84 | test: new RegExp('\\.' + extension + '$'),
85 | use: loader
86 | })
87 | }
88 |
89 | return output
90 | }
91 |
92 | exports.createNotifierCallback = () => {
93 | const notifier = require('node-notifier')
94 |
95 | return (severity, errors) => {
96 | if (severity !== 'error') return
97 |
98 | const error = errors[0]
99 | const filename = error.file && error.file.split('!').pop()
100 |
101 | notifier.notify({
102 | title: packageConfig.name,
103 | message: severity + ': ' + error.name,
104 | subtitle: filename || '',
105 | icon: path.join(__dirname, 'logo.png')
106 | })
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = {
4 | //You can set the vue-loader configuration by yourself.
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const config = require('../config')
5 | const { VueLoaderPlugin } = require('vue-loader')
6 | const vueLoaderConfig = require('./vue-loader.conf')
7 |
8 | function resolve(dir) {
9 | return path.join(__dirname, '..', dir)
10 | }
11 |
12 | const createLintingRule = () => ({
13 | test: /\.(js|vue)$/,
14 | loader: 'eslint-loader',
15 | enforce: 'pre',
16 | include: [resolve('src'), resolve('test')],
17 | options: {
18 | formatter: require('eslint-friendly-formatter'),
19 | emitWarning: !config.dev.showEslintErrorsInOverlay
20 | }
21 | })
22 |
23 | module.exports = {
24 | context: path.resolve(__dirname, '../'),
25 | entry: {
26 | app: ['./node_modules/babel-polyfill/dist/polyfill.js','./src/main.js']
27 | },
28 | output: {
29 | path: config.build.assetsRoot,
30 | filename: '[name].js',
31 | publicPath:
32 | process.env.NODE_ENV === 'production'
33 | ? config.build.assetsPublicPath
34 | : config.dev.assetsPublicPath
35 | },
36 | resolve: {
37 | extensions: ['.js', '.vue', '.json'],
38 | alias: {
39 | '@': resolve('src')
40 | }
41 | },
42 | module: {
43 | rules: [
44 | // ...(config.dev.useEslint ? [createLintingRule()] : []),
45 | {
46 | test: /\.vue$/,
47 | loader: 'vue-loader',
48 | options: vueLoaderConfig
49 | },
50 | {
51 | test: /\.js$/,
52 | loader: 'babel-loader',
53 | include: [
54 | resolve('src'),
55 | resolve('test'),
56 | resolve('mock'),
57 | resolve('node_modules/webpack-dev-server/client')
58 | ]
59 | },
60 | {
61 | test: /\.svg$/,
62 | loader: 'svg-sprite-loader',
63 | include: [resolve('src/icons')],
64 | options: {
65 | symbolId: 'icon-[name]'
66 | }
67 | },
68 | {
69 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
70 | loader: 'url-loader',
71 | exclude: [resolve('src/icons')],
72 | options: {
73 | limit: 10000,
74 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
75 | }
76 | },
77 | {
78 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
79 | loader: 'url-loader',
80 | options: {
81 | limit: 10000,
82 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
83 | }
84 | },
85 | {
86 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
87 | loader: 'url-loader',
88 | options: {
89 | limit: 10000,
90 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
91 | }
92 | }
93 | ]
94 | },
95 | plugins: [new VueLoaderPlugin()],
96 | node: {
97 | // prevent webpack from injecting useless setImmediate polyfill because Vue
98 | // source contains it (although only uses it if it's native).
99 | setImmediate: false,
100 | // prevent webpack from injecting mocks to Node native modules
101 | // that does not make sense for the client
102 | dgram: 'empty',
103 | fs: 'empty',
104 | net: 'empty',
105 | tls: 'empty',
106 | child_process: 'empty'
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const HtmlWebpackPlugin = require('html-webpack-plugin')
9 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
10 | const portfinder = require('portfinder')
11 |
12 | function resolve(dir) {
13 | return path.join(__dirname, '..', dir)
14 | }
15 |
16 | const HOST = process.env.HOST
17 | const PORT = process.env.PORT && Number(process.env.PORT)
18 |
19 | const devWebpackConfig = merge(baseWebpackConfig, {
20 | mode: 'development',
21 | module: {
22 | rules: utils.styleLoaders({
23 | sourceMap: config.dev.cssSourceMap,
24 | usePostCSS: true
25 | })
26 | },
27 | // cheap-module-eval-source-map is faster for development
28 | devtool: config.dev.devtool,
29 |
30 | // these devServer options should be customized in /config/index.js
31 | devServer: {
32 | clientLogLevel: 'warning',
33 | historyApiFallback: true,
34 | disableHostCheck: true,
35 | hot: true,
36 | compress: true,
37 | host: HOST || config.dev.host,
38 | port: PORT || config.dev.port,
39 | open: config.dev.autoOpenBrowser,
40 | overlay: config.dev.errorOverlay
41 | ? { warnings: false, errors: true }
42 | : false,
43 | publicPath: config.dev.assetsPublicPath,
44 | proxy: config.dev.proxyTable,
45 | quiet: true, // necessary for FriendlyErrorsPlugin
46 | watchOptions: {
47 | poll: config.dev.poll
48 | }
49 | },
50 | plugins: [
51 | new webpack.DefinePlugin({
52 | 'process.env': require('../config/dev.env')
53 | }),
54 | new webpack.HotModuleReplacementPlugin(),
55 | // https://github.com/ampedandwired/html-webpack-plugin
56 | new HtmlWebpackPlugin({
57 | filename: 'index.html',
58 | template: 'index.html',
59 | inject: true,
60 | favicon: resolve('favicon.ico'),
61 | title: '量化交易'
62 | })
63 | ]
64 | })
65 |
66 | module.exports = new Promise((resolve, reject) => {
67 | portfinder.basePort = process.env.PORT || config.dev.port
68 | portfinder.getPort((err, port) => {
69 | if (err) {
70 | reject(err)
71 | } else {
72 | // publish the new Port, necessary for e2e tests
73 | process.env.PORT = port
74 | // add port to devServer config
75 | devWebpackConfig.devServer.port = port
76 |
77 | // Add FriendlyErrorsPlugin
78 | devWebpackConfig.plugins.push(
79 | new FriendlyErrorsPlugin({
80 | compilationSuccessInfo: {
81 | messages: [
82 | `Your application is running here: http://${
83 | devWebpackConfig.devServer.host
84 | }:${port}`
85 | ]
86 | },
87 | onErrors: config.dev.notifyOnErrors
88 | ? utils.createNotifierCallback()
89 | : undefined
90 | })
91 | )
92 |
93 | resolve(devWebpackConfig)
94 | }
95 | })
96 | })
97 |
--------------------------------------------------------------------------------
/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const utils = require('./utils')
4 | const webpack = require('webpack')
5 | const config = require('../config')
6 | const merge = require('webpack-merge')
7 | const baseWebpackConfig = require('./webpack.base.conf')
8 | const CopyWebpackPlugin = require('copy-webpack-plugin')
9 | const HtmlWebpackPlugin = require('html-webpack-plugin')
10 | const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
12 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
13 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
14 |
15 | function resolve(dir) {
16 | return path.join(__dirname, '..', dir)
17 | }
18 |
19 | const env = require('../config/prod.env')
20 |
21 | // For NamedChunksPlugin
22 | const seen = new Set()
23 | const nameLength = 4
24 |
25 | const webpackConfig = merge(baseWebpackConfig, {
26 | mode: 'production',
27 | module: {
28 | rules: utils.styleLoaders({
29 | sourceMap: config.build.productionSourceMap,
30 | extract: true,
31 | usePostCSS: true
32 | })
33 | },
34 | devtool: config.build.productionSourceMap ? config.build.devtool : false,
35 | output: {
36 | path: config.build.assetsRoot,
37 | filename: utils.assetsPath('js/[name].[chunkhash:8].js'),
38 | chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js')
39 | },
40 | plugins: [
41 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
42 | new webpack.DefinePlugin({
43 | 'process.env': env
44 | }),
45 | // extract css into its own file
46 | new MiniCssExtractPlugin({
47 | filename: utils.assetsPath('css/[name].[contenthash:8].css'),
48 | chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
49 | }),
50 | // generate dist index.html with correct asset hash for caching.
51 | // you can customize output by editing /index.html
52 | // see https://github.com/ampedandwired/html-webpack-plugin
53 | new HtmlWebpackPlugin({
54 | filename: config.build.index,
55 | template: 'index.html',
56 | inject: true,
57 | favicon: resolve('favicon.ico'),
58 | title: '量化交易',
59 | minify: {
60 | removeComments: true,
61 | collapseWhitespace: true,
62 | removeAttributeQuotes: true
63 | // more options:
64 | // https://github.com/kangax/html-minifier#options-quick-reference
65 | }
66 | // default sort mode uses toposort which cannot handle cyclic deps
67 | // in certain cases, and in webpack 4, chunk order in HTML doesn't
68 | // matter anyway
69 | }),
70 | new ScriptExtHtmlWebpackPlugin({
71 | //`runtime` must same as runtimeChunk name. default is `runtime`
72 | inline: /runtime\..*\.js$/
73 | }),
74 | // keep chunk.id stable when chunk has no name
75 | new webpack.NamedChunksPlugin(chunk => {
76 | if (chunk.name) {
77 | return chunk.name
78 | }
79 | const modules = Array.from(chunk.modulesIterable)
80 | if (modules.length > 1) {
81 | const hash = require('hash-sum')
82 | const joinedHash = hash(modules.map(m => m.id).join('_'))
83 | let len = nameLength
84 | while (seen.has(joinedHash.substr(0, len))) len++
85 | seen.add(joinedHash.substr(0, len))
86 | return `chunk-${joinedHash.substr(0, len)}`
87 | } else {
88 | return modules[0].id
89 | }
90 | }),
91 | // keep module.id stable when vender modules does not change
92 | new webpack.HashedModuleIdsPlugin(),
93 | // copy custom static assets
94 | new CopyWebpackPlugin([
95 | {
96 | from: path.resolve(__dirname, '../static'),
97 | to: config.build.assetsSubDirectory,
98 | ignore: ['.*']
99 | }
100 | ])
101 | ],
102 | optimization: {
103 | splitChunks: {
104 | chunks: 'all',
105 | cacheGroups: {
106 | libs: {
107 | name: 'chunk-libs',
108 | test: /[\\/]node_modules[\\/]/,
109 | priority: 10,
110 | chunks: 'initial' // 只打包初始时依赖的第三方
111 | },
112 | elementUI: {
113 | name: 'chunk-elementUI', // 单独将 elementUI 拆包
114 | priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
115 | test: /[\\/]node_modules[\\/]element-ui[\\/]/
116 | }
117 | }
118 | },
119 | runtimeChunk: 'single',
120 | minimizer: [
121 | new UglifyJsPlugin({
122 | uglifyOptions: {
123 | mangle: {
124 | safari10: true
125 | }
126 | },
127 | sourceMap: config.build.productionSourceMap,
128 | cache: true,
129 | parallel: true
130 | }),
131 | // Compress extracted CSS. We are using this plugin so that possible
132 | // duplicated CSS from different components can be deduped.
133 | new OptimizeCSSAssetsPlugin()
134 | ]
135 | }
136 | })
137 |
138 | if (config.build.productionGzip) {
139 | const CompressionWebpackPlugin = require('compression-webpack-plugin')
140 |
141 | webpackConfig.plugins.push(
142 | new CompressionWebpackPlugin({
143 | algorithm: 'gzip',
144 | test: new RegExp(
145 | '\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
146 | ),
147 | threshold: 10240,
148 | minRatio: 0.8
149 | })
150 | )
151 | }
152 |
153 | if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
154 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
155 | .BundleAnalyzerPlugin
156 |
157 | if (config.build.bundleAnalyzerReport) {
158 | webpackConfig.plugins.push(
159 | new BundleAnalyzerPlugin({
160 | analyzerPort: 8080,
161 | generateStatsFile: false
162 | })
163 | )
164 | }
165 |
166 | if (config.build.generateAnalyzerReport) {
167 | webpackConfig.plugins.push(
168 | new BundleAnalyzerPlugin({
169 | analyzerMode: 'static',
170 | reportFilename: 'bundle-report.html',
171 | openAnalyzer: false
172 | })
173 | )
174 | }
175 | }
176 |
177 | module.exports = webpackConfig
178 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const merge = require('webpack-merge')
3 | const prodEnv = require('./prod.env')
4 |
5 | module.exports = merge(prodEnv, {
6 | NODE_ENV: '"development"',
7 | BASE_API: '"http://localhost:8080"',
8 | })
9 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.2.6
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 | // Paths
10 | assetsSubDirectory: 'static',
11 | assetsPublicPath: './',
12 | proxyTable: {},
13 |
14 | // Various Dev Server settings
15 | host: 'localhost', // can be overwritten by process.env.HOST
16 | port: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
17 | autoOpenBrowser: true,
18 | errorOverlay: true,
19 | notifyOnErrors: false,
20 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
21 |
22 | // Use Eslint Loader?
23 | // If true, your code will be linted during bundling and
24 | // linting errors and warnings will be shown in the console.
25 | useEslint: true,
26 | // If true, eslint errors and warnings will also be shown in the error overlay
27 | // in the browser.
28 | showEslintErrorsInOverlay: false,
29 |
30 | /**
31 | * Source Maps
32 | */
33 |
34 | // https://webpack.js.org/configuration/devtool/#development
35 | devtool: 'cheap-source-map',
36 |
37 | // CSS Sourcemaps off by default because relative paths are "buggy"
38 | // with this option, according to the CSS-Loader README
39 | // (https://github.com/webpack/css-loader#sourcemaps)
40 | // In our experience, they generally work as expected,
41 | // just be aware of this issue when enabling this option.
42 | cssSourceMap: false
43 | },
44 |
45 | build: {
46 | // Template for index.html
47 | index: path.resolve(__dirname, '../dist/index.html'),
48 |
49 | // Paths
50 | assetsRoot: path.resolve(__dirname, '../dist'),
51 | assetsSubDirectory: 'static',
52 |
53 | /**
54 | * You can set by youself according to actual condition
55 | * You will need to set this if you plan to deploy your site under a sub path,
56 | * for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
57 | * then assetsPublicPath should be set to "/bar/".
58 | * In most cases please use '/' !!!
59 | */
60 | assetsPublicPath: '/',
61 |
62 | /**
63 | * Source Maps
64 | */
65 |
66 | productionSourceMap: false,
67 | // https://webpack.js.org/configuration/devtool/#production
68 | devtool: 'source-map',
69 |
70 | // Gzip off by default as many popular static hosts such as
71 | // Surge or Netlify already gzip all static assets for you.
72 | // Before setting to `true`, make sure to:
73 | // npm install --save-dev compression-webpack-plugin
74 | productionGzip: false,
75 | productionGzipExtensions: ['js', 'css'],
76 |
77 | // Run the build command with an extra argument to
78 | // View the bundle analyzer report after build finishes:
79 | // `npm run build --report`
80 | // Set to `true` or `false` to always turn it on or off
81 | bundleAnalyzerReport: process.env.npm_config_report || false,
82 |
83 | // `npm run build:prod --generate_report`
84 | generateAnalyzerReport: process.env.npm_config_generate_report || false
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | module.exports = {
3 | NODE_ENV: '"production"',
4 | BASE_API: '"http://47.95.214.100:8180"',
5 | }
6 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndexOutOfBounds998/quant-admin/e894b3d33ee3abd2e9b2b946b7e1b5676bce16cb/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | <%= htmlWebpackPlugin.options.title %>
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cyy",
3 | "version": "3.9.0",
4 | "license": "MIT",
5 | "description": "A vue admin template with Element UI & axios & iconfont & permission control & lint",
6 | "author": "cyy",
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "build": "node build/build.js",
11 | "build:report": "npm_config_report=true npm run build",
12 | "lint": "eslint --ext .js,.vue src",
13 | "test": "npm run lint",
14 | "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
15 | },
16 | "dependencies": {
17 | "axios": "0.18.0",
18 | "babel-polyfill": "^6.26.0",
19 | "echarts": "^4.2.1",
20 | "element-ui": "2.4.6",
21 | "js-cookie": "2.2.0",
22 | "mockjs": "1.0.1-beta3",
23 | "normalize.css": "7.0.0",
24 | "nprogress": "0.2.0",
25 | "stompjs": "^2.3.3",
26 | "v-charts": "^1.19.0",
27 | "vue": "2.5.17",
28 | "vue-router": "3.0.1",
29 | "vue-trading-view": "^1.0.1",
30 | "vuetify": "^1.5.14",
31 | "vuex": "3.0.1"
32 | },
33 | "devDependencies": {
34 | "autoprefixer": "8.5.0",
35 | "babel-core": "6.26.0",
36 | "babel-eslint": "8.2.6",
37 | "babel-helper-vue-jsx-merge-props": "2.0.3",
38 | "babel-loader": "7.1.5",
39 | "babel-plugin-syntax-jsx": "6.18.0",
40 | "babel-plugin-transform-runtime": "6.23.0",
41 | "babel-plugin-transform-vue-jsx": "3.7.0",
42 | "babel-preset-env": "1.7.0",
43 | "babel-preset-stage-2": "6.24.1",
44 | "chalk": "2.4.1",
45 | "compression-webpack-plugin": "2.0.0",
46 | "copy-webpack-plugin": "4.5.2",
47 | "css-loader": "1.0.0",
48 | "eslint": "4.19.1",
49 | "eslint-friendly-formatter": "4.0.1",
50 | "eslint-loader": "2.0.0",
51 | "eslint-plugin-vue": "4.7.1",
52 | "eventsource-polyfill": "0.9.6",
53 | "file-loader": "1.1.11",
54 | "friendly-errors-webpack-plugin": "1.7.0",
55 | "html-webpack-plugin": "4.0.0-alpha",
56 | "mini-css-extract-plugin": "0.4.1",
57 | "node-notifier": "5.2.1",
58 | "node-sass": "^4.7.2",
59 | "optimize-css-assets-webpack-plugin": "5.0.0",
60 | "ora": "3.0.0",
61 | "path-to-regexp": "2.4.0",
62 | "portfinder": "1.0.16",
63 | "postcss-import": "12.0.0",
64 | "postcss-loader": "2.1.6",
65 | "postcss-url": "7.3.2",
66 | "rimraf": "2.6.2",
67 | "sass-loader": "7.0.3",
68 | "script-ext-html-webpack-plugin": "2.0.1",
69 | "semver": "5.5.0",
70 | "shelljs": "0.8.2",
71 | "svg-sprite-loader": "3.8.0",
72 | "svgo": "1.0.5",
73 | "uglifyjs-webpack-plugin": "1.2.7",
74 | "url-loader": "1.0.1",
75 | "vue-loader": "15.3.0",
76 | "vue-style-loader": "4.1.2",
77 | "vue-template-compiler": "2.5.17",
78 | "webpack": "4.16.5",
79 | "webpack-bundle-analyzer": "3.3.2",
80 | "webpack-cli": "3.1.0",
81 | "webpack-dev-server": "3.1.14",
82 | "webpack-merge": "4.1.4"
83 | },
84 | "engines": {
85 | "node": ">= 6.0.0",
86 | "npm": ">= 3.0.0"
87 | },
88 | "browserslist": [
89 | "> 1%",
90 | "last 2 versions",
91 | "not ie <= 8"
92 | ]
93 | }
94 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/src/api/account.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getAccountList(query) {
4 | return request({
5 | url: '/account/accountsByUid',
6 | method: 'get',
7 | params: query
8 | })
9 | }
10 |
11 | export function getAccounts(query) {
12 | return request({
13 | url: '/account/accounts',
14 | method: 'get',
15 | params: query
16 | })
17 | }
18 | /**
19 | * 获取账户余额
20 | */
21 | export function getBalanceList(query) {
22 | return request({
23 | url: '/balance/getBalanceList',
24 | method: 'get',
25 | params: query
26 | })
27 |
28 | }
29 |
30 | export function createOrUpdateAccount(data) {
31 | return request({
32 | url: '/account/addOrUpdate',
33 | method: 'post',
34 | data: data
35 | })
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/src/api/indicator.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function backTest(pam) {
4 | return request({
5 | url: '/indicator/backTest',
6 | method: 'post',
7 | data: pam
8 | })
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/src/api/login.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function login(username, password) {
4 | return request({
5 | url: '/user/login',
6 | method: 'post',
7 | data: {
8 | username,
9 | password
10 | }
11 | })
12 | }
13 |
14 | export function getInfo(token) {
15 | return request({
16 | url: '/user/info',
17 | method: 'get',
18 | params: { token }
19 | })
20 | }
21 |
22 | export function logout() {
23 | return request({
24 | url: '/user/logout',
25 | method: 'post'
26 | })
27 | }
28 |
--------------------------------------------------------------------------------
/src/api/nodes.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 | /**
3 | * 获取节点信息
4 | */
5 | export function getNodes() {
6 | return request({
7 | url: '/node/nodes',
8 | method: 'get'
9 | })
10 | }
--------------------------------------------------------------------------------
/src/api/order.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getOrdersByRid(params) {
4 | return request({
5 | url: '/order/list',
6 | method: 'get',
7 | params
8 | })
9 | }
10 | export function getProfitInfo(params) {
11 | return request({
12 | url: '/orderProfit/list',
13 | method: 'get',
14 | params
15 | })
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/src/api/robot.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 | /**
3 | * 提交机器人
4 | */
5 | export function addOrUpdateRobot(data) {
6 | return request({
7 | url: '/robot/addOrUpdateRobot',
8 | method: 'post',
9 | data: data
10 | })
11 | }
12 |
13 |
14 | export function getRobotById(data) {
15 | return request({
16 | url: '/robot/getRobotById',
17 | method: 'get',
18 | params: { id: data }
19 | })
20 | }
21 |
22 | export function getRobots() {
23 | return request({
24 | url: '/robot/list',
25 | method: 'get'
26 | })
27 | }
28 |
29 | export function operatingRobot(id) {
30 | return request({
31 | url: '/robot/operatingRobot',
32 | method: 'get',
33 | params: id
34 | })
35 | }
36 |
37 | /**
38 | * 删除机器人
39 | */
40 | export function deleteRobot(id) {
41 | return request({
42 | url: '/robot/deleteRobot',
43 | method: 'get',
44 | params: id
45 | })
46 | }
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/api/strategy.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | /**
4 | * 添加策略
5 | */
6 | export function addOrUpdateStrategy(data) {
7 | return request({
8 | url: '/strategy/addOrUpdateStrategy',
9 | method: 'post',
10 | data: data
11 | })
12 | }
13 | /**
14 | * 获取策略
15 | */
16 | export function getStrategys() {
17 | return request({
18 | url: '/strategy/strategyList',
19 | method: 'get'
20 | })
21 | }
22 | /**
23 | * 获取策略simple
24 | */
25 | export function getSimpleStrategys() {
26 | return request({
27 | url: '/strategy/simpleStrategyList',
28 | method: 'get'
29 | })
30 | }
31 |
32 | /**
33 | * 获取策略
34 | */
35 | export function getStrategyById(params) {
36 | return request({
37 | url: '/strategy/getStrategyById',
38 | method: 'get',
39 | params
40 | })
41 | }
42 |
43 | export function deleteStrategy(params) {
44 | return request({
45 | url: '/strategy/deleteStrategy',
46 | method: 'get',
47 | params
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/src/api/symbol.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 | /**
3 | * 获取节点信息
4 | */
5 | export function getSymbols() {
6 | return request({
7 | url: '/symbol/symbols',
8 | method: 'get'
9 | })
10 | }
--------------------------------------------------------------------------------
/src/api/table.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function getList(params) {
4 | return request({
5 | url: '/table/list',
6 | method: 'get',
7 | params
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
1 | import request from '@/utils/request'
2 |
3 | export function emailEditer(emailEditer) {
4 | return request({
5 | url: '/user/emailEditer',
6 | method: 'post',
7 | data:
8 | emailEditer
9 | })
10 | }
11 |
12 | export function getUserEmail(token) {
13 | return request({
14 | url: '/user/getUserEmail',
15 | method: 'get',
16 | params: {
17 | id: token
18 | }
19 | })
20 | }
21 |
--------------------------------------------------------------------------------
/src/assets/404_images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndexOutOfBounds998/quant-admin/e894b3d33ee3abd2e9b2b946b7e1b5676bce16cb/src/assets/404_images/404.png
--------------------------------------------------------------------------------
/src/assets/404_images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndexOutOfBounds998/quant-admin/e894b3d33ee3abd2e9b2b946b7e1b5676bce16cb/src/assets/404_images/404_cloud.png
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ item.meta.title }}
6 | {{ item.meta.title }}
7 |
8 |
9 |
10 |
11 |
12 |
57 |
58 |
70 |
--------------------------------------------------------------------------------
/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
31 |
32 |
43 |
--------------------------------------------------------------------------------
/src/components/Pagination/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
92 |
93 |
102 |
--------------------------------------------------------------------------------
/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
34 |
35 |
44 |
--------------------------------------------------------------------------------
/src/directive/clipboard/clipboard.js:
--------------------------------------------------------------------------------
1 | // Inspired by https://github.com/Inndy/vue-clipboard2
2 | const Clipboard = require('clipboard')
3 | if (!Clipboard) {
4 | throw new Error('you should npm install `clipboard` --save at first ')
5 | }
6 |
7 | export default {
8 | bind(el, binding) {
9 | if (binding.arg === 'success') {
10 | el._v_clipboard_success = binding.value
11 | } else if (binding.arg === 'error') {
12 | el._v_clipboard_error = binding.value
13 | } else {
14 | const clipboard = new Clipboard(el, {
15 | text() { return binding.value },
16 | action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
17 | })
18 | clipboard.on('success', e => {
19 | const callback = el._v_clipboard_success
20 | callback && callback(e) // eslint-disable-line
21 | })
22 | clipboard.on('error', e => {
23 | const callback = el._v_clipboard_error
24 | callback && callback(e) // eslint-disable-line
25 | })
26 | el._v_clipboard = clipboard
27 | }
28 | },
29 | update(el, binding) {
30 | if (binding.arg === 'success') {
31 | el._v_clipboard_success = binding.value
32 | } else if (binding.arg === 'error') {
33 | el._v_clipboard_error = binding.value
34 | } else {
35 | el._v_clipboard.text = function() { return binding.value }
36 | el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
37 | }
38 | },
39 | unbind(el, binding) {
40 | if (binding.arg === 'success') {
41 | delete el._v_clipboard_success
42 | } else if (binding.arg === 'error') {
43 | delete el._v_clipboard_error
44 | } else {
45 | el._v_clipboard.destroy()
46 | delete el._v_clipboard
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/directive/clipboard/index.js:
--------------------------------------------------------------------------------
1 | import Clipboard from './clipboard'
2 |
3 | const install = function(Vue) {
4 | Vue.directive('Clipboard', Clipboard)
5 | }
6 |
7 | if (window.Vue) {
8 | window.clipboard = Clipboard
9 | Vue.use(install); // eslint-disable-line
10 | }
11 |
12 | Clipboard.install = install
13 | export default Clipboard
14 |
--------------------------------------------------------------------------------
/src/directive/el-dragDialog/drag.js:
--------------------------------------------------------------------------------
1 | export default {
2 | bind(el, binding, vnode) {
3 | const dialogHeaderEl = el.querySelector('.el-dialog__header')
4 | const dragDom = el.querySelector('.el-dialog')
5 | dialogHeaderEl.style.cssText += ';cursor:move;'
6 | dragDom.style.cssText += ';top:0px;'
7 |
8 | // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
9 | const getStyle = (function() {
10 | if (window.document.currentStyle) {
11 | return (dom, attr) => dom.currentStyle[attr]
12 | } else {
13 | return (dom, attr) => getComputedStyle(dom, false)[attr]
14 | }
15 | })()
16 |
17 | dialogHeaderEl.onmousedown = (e) => {
18 | // 鼠标按下,计算当前元素距离可视区的距离
19 | const disX = e.clientX - dialogHeaderEl.offsetLeft
20 | const disY = e.clientY - dialogHeaderEl.offsetTop
21 |
22 | const dragDomWidth = dragDom.offsetWidth
23 | const dragDomHeight = dragDom.offsetHeight
24 |
25 | const screenWidth = document.body.clientWidth
26 | const screenHeight = document.body.clientHeight
27 |
28 | const minDragDomLeft = dragDom.offsetLeft
29 | const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
30 |
31 | const minDragDomTop = dragDom.offsetTop
32 | const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
33 |
34 | // 获取到的值带px 正则匹配替换
35 | let styL = getStyle(dragDom, 'left')
36 | let styT = getStyle(dragDom, 'top')
37 |
38 | if (styL.includes('%')) {
39 | styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
40 | styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
41 | } else {
42 | styL = +styL.replace(/\px/g, '')
43 | styT = +styT.replace(/\px/g, '')
44 | }
45 |
46 | document.onmousemove = function(e) {
47 | // 通过事件委托,计算移动的距离
48 | let left = e.clientX - disX
49 | let top = e.clientY - disY
50 |
51 | // 边界处理
52 | if (-(left) > minDragDomLeft) {
53 | left = -minDragDomLeft
54 | } else if (left > maxDragDomLeft) {
55 | left = maxDragDomLeft
56 | }
57 |
58 | if (-(top) > minDragDomTop) {
59 | top = -minDragDomTop
60 | } else if (top > maxDragDomTop) {
61 | top = maxDragDomTop
62 | }
63 |
64 | // 移动当前元素
65 | dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
66 |
67 | // emit onDrag event
68 | vnode.child.$emit('dragDialog')
69 | }
70 |
71 | document.onmouseup = function(e) {
72 | document.onmousemove = null
73 | document.onmouseup = null
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/directive/el-dragDialog/index.js:
--------------------------------------------------------------------------------
1 | import drag from './drag'
2 |
3 | const install = function(Vue) {
4 | Vue.directive('el-drag-dialog', drag)
5 | }
6 |
7 | if (window.Vue) {
8 | window['el-drag-dialog'] = drag
9 | Vue.use(install); // eslint-disable-line
10 | }
11 |
12 | drag.install = install
13 | export default drag
14 |
--------------------------------------------------------------------------------
/src/directive/el-table/adaptive.js:
--------------------------------------------------------------------------------
1 |
2 | import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'
3 |
4 | /**
5 | * How to use
6 | * ...
7 | * el-table height is must be set
8 | * bottomOffset: 30(default) // The height of the table from the bottom of the page.
9 | */
10 |
11 | const doResize = (el, binding, vnode) => {
12 | const { componentInstance: $table } = vnode
13 |
14 | const { value } = binding
15 |
16 | if (!$table.height) {
17 | throw new Error(`el-$table must set the height. Such as height='100px'`)
18 | }
19 | const bottomOffset = (value && value.bottomOffset) || 30
20 |
21 | if (!$table) return
22 |
23 | const height = window.innerHeight - el.getBoundingClientRect().top - bottomOffset
24 | $table.layout.setHeight(height)
25 | $table.doLayout()
26 | }
27 |
28 | export default {
29 | bind(el, binding, vnode) {
30 | el.resizeListener = () => {
31 | doResize(el, binding, vnode)
32 | }
33 |
34 | addResizeListener(el, el.resizeListener)
35 | },
36 | inserted(el, binding, vnode) {
37 | doResize(el, binding, vnode)
38 | },
39 | unbind(el) {
40 | removeResizeListener(el, el.resizeListener)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/directive/el-table/index.js:
--------------------------------------------------------------------------------
1 |
2 | import adaptive from './adaptive'
3 |
4 | const install = function(Vue) {
5 | Vue.directive('el-height-adaptive-table', adaptive)
6 | }
7 |
8 | if (window.Vue) {
9 | window['el-height-adaptive-table'] = adaptive
10 | Vue.use(install); // eslint-disable-line
11 | }
12 |
13 | adaptive.install = install
14 | export default adaptive
15 |
--------------------------------------------------------------------------------
/src/directive/permission/index.js:
--------------------------------------------------------------------------------
1 | import permission from './permission'
2 |
3 | const install = function(Vue) {
4 | Vue.directive('permission', permission)
5 | }
6 |
7 | if (window.Vue) {
8 | window['permission'] = permission
9 | Vue.use(install); // eslint-disable-line
10 | }
11 |
12 | permission.install = install
13 | export default permission
14 |
--------------------------------------------------------------------------------
/src/directive/permission/permission.js:
--------------------------------------------------------------------------------
1 |
2 | import store from '@/store'
3 |
4 | export default {
5 | inserted(el, binding, vnode) {
6 | const { value } = binding
7 | const roles = store.getters && store.getters.roles
8 |
9 | if (value && value instanceof Array && value.length > 0) {
10 | const permissionRoles = value
11 |
12 | const hasPermission = roles.some(role => {
13 | return permissionRoles.includes(role)
14 | })
15 |
16 | if (!hasPermission) {
17 | el.parentNode && el.parentNode.removeChild(el)
18 | }
19 | } else {
20 | throw new Error(`need roles! Like v-permission="['admin','editor']"`)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/directive/sticky.js:
--------------------------------------------------------------------------------
1 | const vueSticky = {}
2 | let listenAction
3 | vueSticky.install = Vue => {
4 | Vue.directive('sticky', {
5 | inserted(el, binding) {
6 | const params = binding.value || {}
7 | const stickyTop = params.stickyTop || 0
8 | const zIndex = params.zIndex || 1000
9 | const elStyle = el.style
10 |
11 | elStyle.position = '-webkit-sticky'
12 | elStyle.position = 'sticky'
13 | // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
14 | // if (~elStyle.position.indexOf('sticky')) {
15 | // elStyle.top = `${stickyTop}px`;
16 | // elStyle.zIndex = zIndex;
17 | // return
18 | // }
19 | const elHeight = el.getBoundingClientRect().height
20 | const elWidth = el.getBoundingClientRect().width
21 | elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
22 |
23 | const parentElm = el.parentNode || document.documentElement
24 | const placeholder = document.createElement('div')
25 | placeholder.style.display = 'none'
26 | placeholder.style.width = `${elWidth}px`
27 | placeholder.style.height = `${elHeight}px`
28 | parentElm.insertBefore(placeholder, el)
29 |
30 | let active = false
31 |
32 | const getScroll = (target, top) => {
33 | const prop = top ? 'pageYOffset' : 'pageXOffset'
34 | const method = top ? 'scrollTop' : 'scrollLeft'
35 | let ret = target[prop]
36 | if (typeof ret !== 'number') {
37 | ret = window.document.documentElement[method]
38 | }
39 | return ret
40 | }
41 |
42 | const sticky = () => {
43 | if (active) {
44 | return
45 | }
46 | if (!elStyle.height) {
47 | elStyle.height = `${el.offsetHeight}px`
48 | }
49 |
50 | elStyle.position = 'fixed'
51 | elStyle.width = `${elWidth}px`
52 | placeholder.style.display = 'inline-block'
53 | active = true
54 | }
55 |
56 | const reset = () => {
57 | if (!active) {
58 | return
59 | }
60 |
61 | elStyle.position = ''
62 | placeholder.style.display = 'none'
63 | active = false
64 | }
65 |
66 | const check = () => {
67 | const scrollTop = getScroll(window, true)
68 | const offsetTop = el.getBoundingClientRect().top
69 | if (offsetTop < stickyTop) {
70 | sticky()
71 | } else {
72 | if (scrollTop < elHeight + stickyTop) {
73 | reset()
74 | }
75 | }
76 | }
77 | listenAction = () => {
78 | check()
79 | }
80 |
81 | window.addEventListener('scroll', listenAction)
82 | },
83 |
84 | unbind() {
85 | window.removeEventListener('scroll', listenAction)
86 | }
87 | })
88 | }
89 |
90 | export default vueSticky
91 |
92 |
--------------------------------------------------------------------------------
/src/directive/waves/index.js:
--------------------------------------------------------------------------------
1 | import waves from './waves'
2 |
3 | const install = function(Vue) {
4 | Vue.directive('waves', waves)
5 | }
6 |
7 | if (window.Vue) {
8 | window.waves = waves
9 | Vue.use(install); // eslint-disable-line
10 | }
11 |
12 | waves.install = install
13 | export default waves
14 |
--------------------------------------------------------------------------------
/src/directive/waves/waves.css:
--------------------------------------------------------------------------------
1 | .waves-ripple {
2 | position: absolute;
3 | border-radius: 100%;
4 | background-color: rgba(0, 0, 0, 0.15);
5 | background-clip: padding-box;
6 | pointer-events: none;
7 | -webkit-user-select: none;
8 | -moz-user-select: none;
9 | -ms-user-select: none;
10 | user-select: none;
11 | -webkit-transform: scale(0);
12 | -ms-transform: scale(0);
13 | transform: scale(0);
14 | opacity: 1;
15 | }
16 |
17 | .waves-ripple.z-active {
18 | opacity: 0;
19 | -webkit-transform: scale(2);
20 | -ms-transform: scale(2);
21 | transform: scale(2);
22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out;
25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
26 | }
--------------------------------------------------------------------------------
/src/directive/waves/waves.js:
--------------------------------------------------------------------------------
1 | import './waves.css'
2 |
3 | const context = '@@wavesContext'
4 |
5 | function handleClick(el, binding) {
6 | function handle(e) {
7 | const customOpts = Object.assign({}, binding.value)
8 | const opts = Object.assign({
9 | ele: el, // 波纹作用元素
10 | type: 'hit', // hit 点击位置扩散 center中心点扩展
11 | color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
12 | },
13 | customOpts
14 | )
15 | const target = opts.ele
16 | if (target) {
17 | target.style.position = 'relative'
18 | target.style.overflow = 'hidden'
19 | const rect = target.getBoundingClientRect()
20 | let ripple = target.querySelector('.waves-ripple')
21 | if (!ripple) {
22 | ripple = document.createElement('span')
23 | ripple.className = 'waves-ripple'
24 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
25 | target.appendChild(ripple)
26 | } else {
27 | ripple.className = 'waves-ripple'
28 | }
29 | switch (opts.type) {
30 | case 'center':
31 | ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'
32 | ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'
33 | break
34 | default:
35 | ripple.style.top =
36 | (e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop ||
37 | document.body.scrollTop) + 'px'
38 | ripple.style.left =
39 | (e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||
40 | document.body.scrollLeft) + 'px'
41 | }
42 | ripple.style.backgroundColor = opts.color
43 | ripple.className = 'waves-ripple z-active'
44 | return false
45 | }
46 | }
47 |
48 | if (!el[context]) {
49 | el[context] = {
50 | removeHandle: handle
51 | }
52 | } else {
53 | el[context].removeHandle = handle
54 | }
55 |
56 | return handle
57 | }
58 |
59 | export default {
60 | bind(el, binding) {
61 | el.addEventListener('click', handleClick(el, binding), false)
62 | },
63 | update(el, binding) {
64 | el.removeEventListener('click', el[context].removeHandle, false)
65 | el.addEventListener('click', handleClick(el, binding), false)
66 | },
67 | unbind(el) {
68 | el.removeEventListener('click', el[context].removeHandle, false)
69 | el[context] = null
70 | delete el[context]
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/icons/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import SvgIcon from '@/components/SvgIcon' // svg组件
3 |
4 | // register globally
5 | Vue.component('svg-icon', SvgIcon)
6 |
7 | const requireAll = requireContext => requireContext.keys().map(requireContext)
8 | const req = require.context('./svg', false, /\.svg$/)
9 | requireAll(req)
10 |
--------------------------------------------------------------------------------
/src/icons/svg/404.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/bug.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/chart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/clipboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/component.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/dashboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/documentation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/drag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/edit.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/excel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/exit-fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/eye-open.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/icons/svg/eye.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/form.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/fullscreen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/guide 2.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/guide.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/international.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/language.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/list.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/lock.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/message.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/money.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/nested.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/password.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/pdf.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/people.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/peoples.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/qq.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/shopping.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/size.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/star.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/theme.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/tree-table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/tree.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/wechat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/zip.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svgo.yml:
--------------------------------------------------------------------------------
1 | # replace default config
2 |
3 | # multipass: true
4 | # full: true
5 |
6 | plugins:
7 |
8 | # - name
9 | #
10 | # or:
11 | # - name: false
12 | # - name: true
13 | #
14 | # or:
15 | # - name:
16 | # param1: 1
17 | # param2: 2
18 |
19 | - removeAttrs:
20 | attrs:
21 | - 'fill'
22 | - 'fill-rule'
23 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets
4 |
5 | import ElementUI from 'element-ui'
6 | import 'element-ui/lib/theme-chalk/index.css'
7 | import locale from 'element-ui/lib/locale/lang/en' // lang i18n
8 |
9 | import '@/styles/index.scss' // global css
10 |
11 | import App from './App'
12 | import store from './store'
13 | import router from './router'
14 |
15 | import '@/icons' // icon
16 | import '@/permission' // permission control
17 | import Vuetify from 'vuetify'
18 | // index.js or main.js
19 | import 'vuetify/dist/vuetify.min.css'
20 | /**
21 | * This project originally used easy-mock to simulate data,
22 | * but its official service is very unstable,
23 | * and you can build your own service if you need it.
24 | * So here I use Mock.js for local emulation,
25 | * it will intercept your request, so you won't see the request in the network.
26 | * If you remove `../mock` it will automatically request easy-mock data.
27 | */
28 |
29 | Vue.use(ElementUI, Vuetify, { locale })
30 |
31 | Vue.config.productionTip = false
32 |
33 | new Vue({
34 | el: '#app',
35 | router,
36 | store,
37 | render: h => h(App)
38 | })
39 |
--------------------------------------------------------------------------------
/src/permission.js:
--------------------------------------------------------------------------------
1 | import router from './router'
2 | import store from './store'
3 | import NProgress from 'nprogress' // progress bar
4 | import 'nprogress/nprogress.css' // progress bar style
5 | import { Message } from 'element-ui'
6 | import { getToken } from '@/utils/auth' // getToken from cookie
7 |
8 | NProgress.configure({ showSpinner: false })// NProgress configuration
9 |
10 | const whiteList = ['/login'] // 不重定向白名单
11 | router.beforeEach((to, from, next) => {
12 | NProgress.start()
13 | if (getToken()) {
14 | if (to.path === '/login') {
15 | next({ path: '/' })
16 | NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
17 | } else {
18 | if (store.getters.roles.length === 0) {
19 | store.dispatch('GetInfo').then(res => { // 拉取用户信息
20 | next()
21 | }).catch((err) => {
22 | store.dispatch('FedLogOut').then(() => {
23 | Message.error(err || 'Verification failed, please login again')
24 | next({ path: '/' })
25 | })
26 | })
27 | } else {
28 | next()
29 | }
30 | }
31 | } else {
32 | if (whiteList.indexOf(to.path) !== -1) {
33 | next()
34 | } else {
35 | next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
36 | NProgress.done()
37 | }
38 | }
39 | })
40 |
41 | router.afterEach(() => {
42 | NProgress.done() // 结束Progress
43 | })
44 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | // in development-env not use lazy-loading, because lazy-loading too many pages will cause webpack hot update too slow. so only in production use lazy-loading;
5 | // detail: https://panjiachen.github.io/vue-element-admin-site/#/lazy-loading
6 |
7 | Vue.use(Router)
8 |
9 | /* Layout */
10 | import Layout from '../views/layout/Layout'
11 |
12 | /**
13 | * hidden: true if `hidden:true` will not show in the sidebar(default is false)
14 | * alwaysShow: true if set true, will always show the root menu, whatever its child routes length
15 | * if not set alwaysShow, only more than one route under the children
16 | * it will becomes nested mode, otherwise not show the root menu
17 | * redirect: noredirect if `redirect:noredirect` will no redirect in the breadcrumb
18 | * name:'router-name' the name is used by (must set!!!)
19 | * meta : {
20 | title: 'title' the name show in subMenu and breadcrumb (recommend set)
21 | icon: 'svg-name' the icon show in the sidebar
22 | breadcrumb: false if false, the item will hidden in breadcrumb(default is true)
23 | }
24 | **/
25 | export const constantRouterMap = [
26 | { path: '/login', component: () => import('@/views/login/index'), hidden: true },
27 | { path: '/404', component: () => import('@/views/404'), hidden: true },
28 |
29 | {
30 | path: '/',
31 | component: Layout,
32 | redirect: '/dashboard',
33 | name: 'Dashboard',
34 | hidden: true,
35 | children: [{
36 | path: 'dashboard',
37 | component: () => import('@/views/dashboard/index'),
38 | meta: { title: '控制面板' }
39 | }]
40 | },
41 | {
42 | path: '/strategy',
43 | component: Layout,
44 | meta: { title: '策略中心', icon: 'example' },
45 | children: [
46 | {
47 | path: 'index',
48 | name: 'Index',
49 | component: () => import('@/views/strategy/index'),
50 | meta: { title: '简单策略', icon: 'table' }
51 | },
52 | {
53 | path: 'indicator',
54 | name: 'Indicator',
55 | component: () => import('@/views/strategy/indicator'),
56 | meta: { title: '指标策略', icon: 'tree' }
57 | }, {
58 | path: 'list',
59 | name: 'List',
60 | component: () => import('@/views/strategy/list'),
61 | meta: { title: '我的策略', icon: 'excel' }
62 | }
63 | ]
64 | },
65 | {
66 | path: '/robot',
67 | component: Layout,
68 | meta: { title: '托管中心', icon: 'people' },
69 | children: [
70 | {
71 | path: 'list',
72 | name: 'List',
73 | component: () => import('@/views/robot/list'),
74 | meta: { title: '我的机器人', icon: 'peoples' }
75 | }, {
76 | path: 'index',
77 | name: 'Index',
78 | component: () => import('@/views/robot/index'),
79 | meta: { title: '添加机器人', icon: 'people' }
80 | }, {
81 | path: 'info',
82 | name: 'Info',
83 | hidden: true,
84 | component: () => import('@/views/robot/info'),
85 | meta: { title: '运行监控', icon: 'table' }
86 | }
87 | ]
88 | },
89 | {
90 | path: '/account',
91 | component: Layout,
92 | meta: {
93 | title: '配置中心',
94 | icon: 'nested'
95 | },
96 | children: [
97 | {
98 | path: 'index',
99 | name: 'Index',
100 | component: () => import('@/views/account/index'),
101 | meta: { title: 'api配置', icon: 'table' }
102 | }, {
103 | path: 'email',
104 | name: 'Email',
105 | component: () => import('@/views/account/email'),
106 | meta: { title: '邮件配置', icon: 'email' }
107 | }, {
108 | path: 'info',
109 | name: 'Info',
110 | hidden: true,
111 | component: () => import('@/views/account/info'),
112 | meta: { title: '账户信息', icon: 'table' }
113 | }
114 | ]
115 | },
116 | {
117 | path: '/order',
118 | component: Layout,
119 | hidden: true,
120 | meta: {
121 | title: '订单',
122 | icon: 'nested'
123 | },
124 | children: [
125 | {
126 | path: 'list',
127 | name: 'List',
128 | hidden: true,
129 | component: () => import('@/views/order/list'),
130 | meta: { title: '订单列表', icon: 'table' }
131 | },
132 | {
133 | path: 'profit',
134 | name: 'Profit',
135 | hidden: true,
136 | component: () => import('@/views/order/profit'),
137 | meta: { title: '盈亏列表', icon: 'table' }
138 | }
139 | ]
140 | },
141 |
142 | { path: '*', redirect: '/404', hidden: true }
143 | ]
144 |
145 | export default new Router({
146 | // mode: 'history', //后端支持可开
147 | scrollBehavior: () => ({ y: 0 }),
148 | routes: constantRouterMap
149 | })
150 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | const getters = {
2 | sidebar: state => state.app.sidebar,
3 | device: state => state.app.device,
4 | token: state => state.user.token,
5 | avatar: state => state.user.avatar,
6 | name: state => state.user.name,
7 | roles: state => state.user.roles
8 | }
9 | export default getters
10 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import app from './modules/app'
4 | import user from './modules/user'
5 | import getters from './getters'
6 |
7 | Vue.use(Vuex)
8 |
9 | const store = new Vuex.Store({
10 | modules: {
11 | app,
12 | user
13 | },
14 | getters
15 | })
16 |
17 | export default store
18 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const app = {
4 | state: {
5 | sidebar: {
6 | opened: !+Cookies.get('sidebarStatus'),
7 | withoutAnimation: false
8 | },
9 | device: 'desktop'
10 | },
11 | mutations: {
12 | TOGGLE_SIDEBAR: state => {
13 | if (state.sidebar.opened) {
14 | Cookies.set('sidebarStatus', 1)
15 | } else {
16 | Cookies.set('sidebarStatus', 0)
17 | }
18 | state.sidebar.opened = !state.sidebar.opened
19 | state.sidebar.withoutAnimation = false
20 | },
21 | CLOSE_SIDEBAR: (state, withoutAnimation) => {
22 | Cookies.set('sidebarStatus', 1)
23 | state.sidebar.opened = false
24 | state.sidebar.withoutAnimation = withoutAnimation
25 | },
26 | TOGGLE_DEVICE: (state, device) => {
27 | state.device = device
28 | }
29 | },
30 | actions: {
31 | ToggleSideBar: ({ commit }) => {
32 | commit('TOGGLE_SIDEBAR')
33 | },
34 | CloseSideBar({ commit }, { withoutAnimation }) {
35 | commit('CLOSE_SIDEBAR', withoutAnimation)
36 | },
37 | ToggleDevice({ commit }, device) {
38 | commit('TOGGLE_DEVICE', device)
39 | }
40 | }
41 | }
42 |
43 | export default app
44 |
--------------------------------------------------------------------------------
/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import { login, logout, getInfo } from '@/api/login'
2 | import { getToken, setToken, removeToken } from '@/utils/auth'
3 |
4 | const user = {
5 | state: {
6 | token: getToken(),
7 | name: '',
8 | avatar: '',
9 | roles: []
10 | },
11 |
12 | mutations: {
13 | SET_TOKEN: (state, token) => {
14 | state.token = token
15 | },
16 | SET_NAME: (state, name) => {
17 | state.name = name
18 | },
19 | SET_AVATAR: (state, avatar) => {
20 | state.avatar = avatar
21 | },
22 | SET_ROLES: (state, roles) => {
23 | state.roles = roles
24 | }
25 | },
26 |
27 | actions: {
28 | // 登录
29 | Login({ commit }, userInfo) {
30 | const username = userInfo.username.trim()
31 | return new Promise((resolve, reject) => {
32 | login(username, userInfo.password).then(response => {
33 | const data = response.data
34 | setToken(data)
35 | commit('SET_TOKEN', data)
36 | resolve()
37 | }).catch(error => {
38 | reject(error)
39 | })
40 | })
41 | },
42 |
43 | // 获取用户信息
44 | GetInfo({ commit, state }) {
45 | return new Promise((resolve, reject) => {
46 | getInfo(state.token).then(response => {
47 | const data = response.data
48 | if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
49 | commit('SET_ROLES', data.roles)
50 | } else {
51 | reject('getInfo: roles must be a non-null array !')
52 | }
53 | commit('SET_NAME', data.username)
54 | commit('SET_AVATAR', data.avatar)
55 | resolve(response)
56 | }).catch(error => {
57 | reject(error)
58 | })
59 | })
60 | },
61 |
62 | // 登出
63 | LogOut({ commit, state }) {
64 | return new Promise((resolve, reject) => {
65 | logout(state.token).then(() => {
66 | commit('SET_TOKEN', '')
67 | commit('SET_ROLES', [])
68 | removeToken()
69 | resolve()
70 | }).catch(error => {
71 | reject(error)
72 | })
73 | })
74 | },
75 |
76 | // 前端 登出
77 | FedLogOut({ commit }) {
78 | return new Promise(resolve => {
79 | commit('SET_TOKEN', '')
80 | removeToken()
81 | resolve()
82 | })
83 | }
84 | }
85 | }
86 |
87 | export default user
88 |
--------------------------------------------------------------------------------
/src/styles/btn.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 |
3 | @mixin colorBtn($color) {
4 | background: $color;
5 |
6 | &:hover {
7 | color: $color;
8 |
9 | &:before,
10 | &:after {
11 | background: $color;
12 | }
13 | }
14 | }
15 |
16 | .blue-btn {
17 | @include colorBtn($blue)
18 | }
19 |
20 | .light-blue-btn {
21 | @include colorBtn($light-blue)
22 | }
23 |
24 | .red-btn {
25 | @include colorBtn($red)
26 | }
27 |
28 | .pink-btn {
29 | @include colorBtn($pink)
30 | }
31 |
32 | .green-btn {
33 | @include colorBtn($green)
34 | }
35 |
36 | .tiffany-btn {
37 | @include colorBtn($tiffany)
38 | }
39 |
40 | .yellow-btn {
41 | @include colorBtn($yellow)
42 | }
43 |
44 | .pan-btn {
45 | font-size: 14px;
46 | color: #fff;
47 | padding: 14px 36px;
48 | border-radius: 8px;
49 | border: none;
50 | outline: none;
51 | transition: 600ms ease all;
52 | position: relative;
53 | display: inline-block;
54 |
55 | &:hover {
56 | background: #fff;
57 |
58 | &:before,
59 | &:after {
60 | width: 100%;
61 | transition: 600ms ease all;
62 | }
63 | }
64 |
65 | &:before,
66 | &:after {
67 | content: '';
68 | position: absolute;
69 | top: 0;
70 | right: 0;
71 | height: 2px;
72 | width: 0;
73 | transition: 400ms ease all;
74 | }
75 |
76 | &::after {
77 | right: inherit;
78 | top: inherit;
79 | left: 0;
80 | bottom: 0;
81 | }
82 | }
83 |
84 | .custom-button {
85 | display: inline-block;
86 | line-height: 1;
87 | white-space: nowrap;
88 | cursor: pointer;
89 | background: #fff;
90 | color: #fff;
91 | -webkit-appearance: none;
92 | text-align: center;
93 | box-sizing: border-box;
94 | outline: 0;
95 | margin: 0;
96 | padding: 10px 15px;
97 | font-size: 14px;
98 | border-radius: 4px;
99 | }
100 |
--------------------------------------------------------------------------------
/src/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | //覆盖一些element-ui样式
2 |
3 | .el-breadcrumb__inner,
4 | .el-breadcrumb__inner a {
5 | font-weight: 400 !important;
6 | }
7 |
8 | .el-upload {
9 | input[type="file"] {
10 | display: none !important;
11 | }
12 | }
13 |
14 | .el-upload__input {
15 | display: none;
16 | }
17 |
18 | .cell {
19 | .el-tag {
20 | margin-right: 0px;
21 | }
22 | }
23 |
24 | .small-padding {
25 | .cell {
26 | padding-left: 5px;
27 | padding-right: 5px;
28 | }
29 | }
30 |
31 | .fixed-width {
32 | .el-button--mini {
33 | padding: 7px 10px;
34 | width: 60px;
35 | }
36 | }
37 |
38 | .status-col {
39 | .cell {
40 | padding: 0 10px;
41 | text-align: center;
42 |
43 | .el-tag {
44 | margin-right: 0px;
45 | }
46 | }
47 | }
48 |
49 | //暂时性解决dialog 问题 https://github.com/ElemeFE/element/issues/2461
50 | .el-dialog {
51 | transform: none;
52 | left: 0;
53 | position: relative;
54 | margin: 0 auto;
55 | }
56 |
57 | //文章页textarea修改样式
58 | .article-textarea {
59 | textarea {
60 | padding-right: 40px;
61 | resize: none;
62 | border: none;
63 | border-radius: 0px;
64 | border-bottom: 1px solid #bfcbd9;
65 | }
66 | }
67 |
68 | //element ui upload
69 | .upload-container {
70 | .el-upload {
71 | width: 100%;
72 |
73 | .el-upload-dragger {
74 | width: 100%;
75 | height: 200px;
76 | }
77 | }
78 | }
79 |
80 | //dropdown
81 | .el-dropdown-menu {
82 | a {
83 | display: block
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/styles/element-variables.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * I think element-ui's default theme color is too light for long-term use.
3 | * So I modified the default color and you can modify it to your liking.
4 | **/
5 |
6 | /* theme color */
7 | $--color-primary: #1890ff;
8 | $--color-success: #13ce66;
9 | $--color-warning: #FFBA00;
10 | $--color-danger: #ff4949;
11 | // $--color-info: #1E1E1E;
12 |
13 | $--button-font-weight: 400;
14 |
15 | // $--color-text-regular: #1f2d3d;
16 |
17 | $--border-color-light: #dfe4ed;
18 | $--border-color-lighter: #e6ebf5;
19 |
20 | $--table-border:1px solid#dfe6ec;
21 |
22 | /* icon font path, required */
23 | $--font-path: '~element-ui/lib/theme-chalk/fonts';
24 |
25 | @import "~element-ui/packages/theme-chalk/src/index";
26 |
27 | // the :export directive is the magic sauce for webpack
28 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
29 | :export {
30 | theme: $--color-primary;
31 | }
32 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 | @import './mixin.scss';
3 | @import './transition.scss';
4 | @import './element-ui.scss';
5 | @import './sidebar.scss';
6 | @import './btn.scss';
7 |
8 | body {
9 | height: 100%;
10 | -moz-osx-font-smoothing: grayscale;
11 | -webkit-font-smoothing: antialiased;
12 | text-rendering: optimizeLegibility;
13 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
14 | }
15 |
16 | label {
17 | font-weight: 700;
18 | }
19 |
20 | html {
21 | height: 100%;
22 | box-sizing: border-box;
23 | }
24 |
25 | #app {
26 | height: 100%;
27 | }
28 |
29 | *,
30 | *:before,
31 | *:after {
32 | box-sizing: inherit;
33 | }
34 |
35 | .no-padding {
36 | padding: 0px !important;
37 | }
38 |
39 | .padding-content {
40 | padding: 4px 0;
41 | }
42 |
43 | a:focus,
44 | a:active {
45 | outline: none;
46 | }
47 |
48 | a,
49 | a:focus,
50 | a:hover {
51 | cursor: pointer;
52 | color: inherit;
53 | text-decoration: none;
54 | }
55 |
56 | div:focus {
57 | outline: none;
58 | }
59 |
60 | .fr {
61 | float: right;
62 | }
63 |
64 | .fl {
65 | float: left;
66 | }
67 |
68 | .pr-5 {
69 | padding-right: 5px;
70 | }
71 |
72 | .pl-5 {
73 | padding-left: 5px;
74 | }
75 |
76 | .block {
77 | display: block;
78 | }
79 |
80 | .pointer {
81 | cursor: pointer;
82 | }
83 |
84 | .inlineBlock {
85 | display: block;
86 | }
87 |
88 | .clearfix {
89 | &:after {
90 | visibility: hidden;
91 | display: block;
92 | font-size: 0;
93 | content: " ";
94 | clear: both;
95 | height: 0;
96 | }
97 | }
98 |
99 | code {
100 | background: #eef1f6;
101 | padding: 15px 16px;
102 | margin-bottom: 20px;
103 | display: block;
104 | line-height: 36px;
105 | font-size: 15px;
106 | font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
107 |
108 | a {
109 | color: #337ab7;
110 | cursor: pointer;
111 |
112 | &:hover {
113 | color: rgb(32, 160, 255);
114 | }
115 | }
116 | }
117 |
118 | .warn-content {
119 | background: rgba(66, 185, 131, .1);
120 | border-radius: 2px;
121 | padding: 16px;
122 | padding: 1rem;
123 | line-height: 1.6rem;
124 | word-spacing: .05rem;
125 |
126 | a {
127 | color: #42b983;
128 | font-weight: 600;
129 | }
130 | }
131 |
132 | //main-container全局样式
133 | .app-container {
134 | padding: 20px;
135 | }
136 |
137 | .components-container {
138 | margin: 30px 50px;
139 | position: relative;
140 | }
141 |
142 | .pagination-container {
143 | margin-top: 30px;
144 | }
145 |
146 | .text-center {
147 | text-align: center
148 | }
149 |
150 | .sub-navbar {
151 | height: 50px;
152 | line-height: 50px;
153 | position: relative;
154 | width: 100%;
155 | text-align: right;
156 | padding-right: 20px;
157 | transition: 600ms ease position;
158 | background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%);
159 |
160 | .subtitle {
161 | font-size: 20px;
162 | color: #fff;
163 | }
164 |
165 | &.draft {
166 | background: #d0d0d0;
167 | }
168 |
169 | &.deleted {
170 | background: #d0d0d0;
171 | }
172 | }
173 |
174 | .link-type,
175 | .link-type:focus {
176 | color: #337ab7;
177 | cursor: pointer;
178 |
179 | &:hover {
180 | color: rgb(32, 160, 255);
181 | }
182 | }
183 |
184 | .filter-container {
185 | padding-bottom: 10px;
186 |
187 | .filter-item {
188 | display: inline-block;
189 | vertical-align: middle;
190 | margin-bottom: 10px;
191 | }
192 | }
193 |
194 | //refine vue-multiselect plugin
195 | .multiselect {
196 | line-height: 16px;
197 | }
198 |
199 | .multiselect--active {
200 | z-index: 1000 !important;
201 | }
202 |
--------------------------------------------------------------------------------
/src/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix {
2 | &:after {
3 | content: "";
4 | display: table;
5 | clear: both;
6 | }
7 | }
8 |
9 | @mixin scrollBar {
10 | &::-webkit-scrollbar-track-piece {
11 | background: #d3dce6;
12 | }
13 |
14 | &::-webkit-scrollbar {
15 | width: 6px;
16 | }
17 |
18 | &::-webkit-scrollbar-thumb {
19 | background: #99a9bf;
20 | border-radius: 20px;
21 | }
22 | }
23 |
24 | @mixin relative {
25 | position: relative;
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
30 | @mixin pct($pct) {
31 | width: #{$pct};
32 | position: relative;
33 | margin: 0 auto;
34 | }
35 |
36 | @mixin triangle($width, $height, $color, $direction) {
37 | $width: $width/2;
38 | $color-border-style: $height solid $color;
39 | $transparent-border-style: $width solid transparent;
40 | height: 0;
41 | width: 0;
42 |
43 | @if $direction==up {
44 | border-bottom: $color-border-style;
45 | border-left: $transparent-border-style;
46 | border-right: $transparent-border-style;
47 | }
48 |
49 | @else if $direction==right {
50 | border-left: $color-border-style;
51 | border-top: $transparent-border-style;
52 | border-bottom: $transparent-border-style;
53 | }
54 |
55 | @else if $direction==down {
56 | border-top: $color-border-style;
57 | border-left: $transparent-border-style;
58 | border-right: $transparent-border-style;
59 | }
60 |
61 | @else if $direction==left {
62 | border-right: $color-border-style;
63 | border-top: $transparent-border-style;
64 | border-bottom: $transparent-border-style;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/styles/sidebar.scss:
--------------------------------------------------------------------------------
1 | #app {
2 |
3 | // 主体区域 Main container
4 | .main-container {
5 | min-height: 100%;
6 | transition: margin-left .28s;
7 | margin-left: $sideBarWidth;
8 | position: relative;
9 | }
10 |
11 | // 侧边栏 Sidebar container
12 | .sidebar-container {
13 | transition: width 0.28s;
14 | width: $sideBarWidth !important;
15 | height: 100%;
16 | position: fixed;
17 | font-size: 0px;
18 | top: 0;
19 | bottom: 0;
20 | left: 0;
21 | z-index: 1001;
22 | overflow: hidden;
23 |
24 | //reset element-ui css
25 | .horizontal-collapse-transition {
26 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
27 | }
28 |
29 | .scrollbar-wrapper {
30 | overflow-x: hidden !important;
31 |
32 | .el-scrollbar__view {
33 | height: 100%;
34 | }
35 | }
36 |
37 | .el-scrollbar__bar.is-vertical {
38 | right: 0px;
39 | }
40 |
41 | .el-scrollbar {
42 | height: 100%;
43 | }
44 |
45 | &.has-logo {
46 | .el-scrollbar {
47 | height: calc(100% - 50px);
48 | }
49 | }
50 |
51 | .is-horizontal {
52 | display: none;
53 | }
54 |
55 | a {
56 | display: inline-block;
57 | width: 100%;
58 | overflow: hidden;
59 | }
60 |
61 | .svg-icon {
62 | margin-right: 16px;
63 | }
64 |
65 | .el-menu {
66 | border: none;
67 | height: 100%;
68 | width: 100% !important;
69 | }
70 |
71 | // menu hover
72 | .submenu-title-noDropdown,
73 | .el-submenu__title {
74 | &:hover {
75 | background-color: $menuHover !important;
76 | }
77 | }
78 |
79 | .is-active>.el-submenu__title {
80 | color: $subMenuActiveText !important;
81 | }
82 |
83 | & .nest-menu .el-submenu>.el-submenu__title,
84 | & .el-submenu .el-menu-item {
85 | min-width: $sideBarWidth !important;
86 | background-color: $subMenuBg !important;
87 |
88 | &:hover {
89 | background-color: $subMenuHover !important;
90 | }
91 | }
92 | }
93 |
94 | .hideSidebar {
95 | .sidebar-container {
96 | width: 54px !important;
97 | }
98 |
99 | .main-container {
100 | margin-left: 54px;
101 | }
102 |
103 | .svg-icon {
104 | margin-right: 0px;
105 | }
106 |
107 | .submenu-title-noDropdown {
108 | padding: 0 !important;
109 | position: relative;
110 |
111 | .el-tooltip {
112 | padding: 0 !important;
113 |
114 | .svg-icon {
115 | margin-left: 20px;
116 | }
117 | }
118 | }
119 |
120 | .el-submenu {
121 | overflow: hidden;
122 |
123 | &>.el-submenu__title {
124 | padding: 0 !important;
125 |
126 | .svg-icon {
127 | margin-left: 20px;
128 | }
129 |
130 | .el-submenu__icon-arrow {
131 | display: none;
132 | }
133 | }
134 | }
135 |
136 | .el-menu--collapse {
137 | .el-submenu {
138 | &>.el-submenu__title {
139 | &>span {
140 | height: 0;
141 | width: 0;
142 | overflow: hidden;
143 | visibility: hidden;
144 | display: inline-block;
145 | }
146 | }
147 | }
148 | }
149 | }
150 |
151 | .el-menu--collapse .el-menu .el-submenu {
152 | min-width: $sideBarWidth !important;
153 | }
154 |
155 | // 适配移动端, Mobile responsive
156 | .mobile {
157 | .main-container {
158 | margin-left: 0px;
159 | }
160 |
161 | .sidebar-container {
162 | transition: transform .28s;
163 | width: $sideBarWidth !important;
164 | }
165 |
166 | &.hideSidebar {
167 | .sidebar-container {
168 | pointer-events: none;
169 | transition-duration: 0.3s;
170 | transform: translate3d(-$sideBarWidth, 0, 0);
171 | }
172 | }
173 | }
174 |
175 | .withoutAnimation {
176 |
177 | .main-container,
178 | .sidebar-container {
179 | transition: none;
180 | }
181 | }
182 | }
183 |
184 | // when menu collapsed
185 | .el-menu--vertical {
186 | &>.el-menu {
187 | .svg-icon {
188 | margin-right: 16px;
189 | }
190 | }
191 |
192 | .nest-menu .el-submenu>.el-submenu__title,
193 | .el-menu-item {
194 | &:hover {
195 | // you can use $subMenuHover
196 | background-color: $menuHover !important;
197 | }
198 | }
199 |
200 | // the scroll bar appears when the subMenu is too long
201 | >.el-menu--popup {
202 | max-height: 100vh;
203 | overflow-y: auto;
204 |
205 | &::-webkit-scrollbar-track-piece {
206 | background: #d3dce6;
207 | }
208 |
209 | &::-webkit-scrollbar {
210 | width: 6px;
211 | }
212 |
213 | &::-webkit-scrollbar-thumb {
214 | background: #99a9bf;
215 | border-radius: 20px;
216 | }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/styles/transition.scss:
--------------------------------------------------------------------------------
1 | //global transition css
2 |
3 | /*fade*/
4 | .fade-enter-active,
5 | .fade-leave-active {
6 | transition: opacity 0.28s;
7 | }
8 |
9 | .fade-enter,
10 | .fade-leave-active {
11 | opacity: 0;
12 | }
13 |
14 | /*fade-transform*/
15 | .fade-transform-leave-active,
16 | .fade-transform-enter-active {
17 | transition: all .5s;
18 | }
19 |
20 | .fade-transform-enter {
21 | opacity: 0;
22 | transform: translateX(-30px);
23 | }
24 |
25 | .fade-transform-leave-to {
26 | opacity: 0;
27 | transform: translateX(30px);
28 | }
29 |
30 | /*breadcrumb transition*/
31 | .breadcrumb-enter-active,
32 | .breadcrumb-leave-active {
33 | transition: all .5s;
34 | }
35 |
36 | .breadcrumb-enter,
37 | .breadcrumb-leave-active {
38 | opacity: 0;
39 | transform: translateX(20px);
40 | }
41 |
42 | .breadcrumb-move {
43 | transition: all .5s;
44 | }
45 |
46 | .breadcrumb-leave-active {
47 | position: absolute;
48 | }
49 |
--------------------------------------------------------------------------------
/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 | // base color
2 | $blue:#324157;
3 | $light-blue:#3A71A8;
4 | $red:#C03639;
5 | $pink: #E65D6E;
6 | $green: #30B08F;
7 | $tiffany: #4AB7BD;
8 | $yellow:#FEC171;
9 | $panGreen: #30B08F;
10 |
11 | //sidebar
12 | $menuText:#bfcbd9;
13 | $menuActiveText:#409EFF;
14 | $subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951
15 |
16 | $menuBg:#304156;
17 | $menuHover:#263445;
18 |
19 | $subMenuBg:#1f2d3d;
20 | $subMenuHover:#001528;
21 |
22 | $sideBarWidth: 210px;
23 |
24 | // the :export directive is the magic sauce for webpack
25 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
26 | :export {
27 | menuText: $menuText;
28 | menuActiveText: $menuActiveText;
29 | subMenuActiveText: $subMenuActiveText;
30 | menuBg: $menuBg;
31 | menuHover: $menuHover;
32 | subMenuBg: $subMenuBg;
33 | subMenuHover: $subMenuHover;
34 | sideBarWidth: $sideBarWidth;
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const TokenKey = 'uid'
4 |
5 | export function getToken() {
6 | return Cookies.get(TokenKey)
7 | }
8 |
9 | export function setToken(token) {
10 | return Cookies.set(TokenKey, token)
11 | }
12 |
13 | export function removeToken() {
14 | return Cookies.remove(TokenKey)
15 | }
16 |
--------------------------------------------------------------------------------
/src/utils/clipboard.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Clipboard from 'clipboard'
3 |
4 | function clipboardSuccess() {
5 | Vue.prototype.$message({
6 | message: 'Copy successfully',
7 | type: 'success',
8 | duration: 1500
9 | })
10 | }
11 |
12 | function clipboardError() {
13 | Vue.prototype.$message({
14 | message: 'Copy failed',
15 | type: 'error'
16 | })
17 | }
18 |
19 | export default function handleClipboard(text, event) {
20 | const clipboard = new Clipboard(event.target, {
21 | text: () => text
22 | })
23 | clipboard.on('success', () => {
24 | clipboardSuccess()
25 | clipboard.off('error')
26 | clipboard.off('success')
27 | clipboard.destroy()
28 | })
29 | clipboard.on('error', () => {
30 | clipboardError()
31 | clipboard.off('error')
32 | clipboard.off('success')
33 | clipboard.destroy()
34 | })
35 | clipboard.onClick(event)
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils/date.js:
--------------------------------------------------------------------------------
1 |
2 | // date.js
3 | export function formatDate(date, fmt) {
4 | if (/(y+)/.test(fmt)) {
5 | fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
6 | }
7 | let o = {
8 | 'M+': date.getMonth() + 1,
9 | 'd+': date.getDate(),
10 | 'h+': date.getHours(),
11 | 'm+': date.getMinutes(),
12 | 's+': date.getSeconds()
13 | };
14 | for (let k in o) {
15 | if (new RegExp(`(${k})`).test(fmt)) {
16 | let str = o[k] + '';
17 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
18 | }
19 | }
20 | return fmt;
21 | };
22 |
23 | function padLeftZero(str) {
24 | return ('00' + str).substr(str.length);
25 | }
--------------------------------------------------------------------------------
/src/utils/errorLog.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import store from '@/store'
3 | import { isString, isArray } from '@/utils/validate'
4 | import settings from '@/settings'
5 |
6 | // you can set in settings.js
7 | // errorLog:'production' | ['production','development']
8 | const { errorLog: needErrorLog } = settings
9 |
10 | function checkNeed(arg) {
11 | const env = process.env.NODE_ENV
12 | if (isString(needErrorLog)) {
13 | return env === needErrorLog
14 | }
15 | if (isArray(needErrorLog)) {
16 | return needErrorLog.includes(env)
17 | }
18 | return false
19 | }
20 |
21 | if (checkNeed()) {
22 | Vue.config.errorHandler = function(err, vm, info, a) {
23 | // Don't ask me why I use Vue.nextTick, it just a hack.
24 | // detail see https://forum.vuejs.org/t/dispatch-in-vue-config-errorhandler-has-some-problem/23500
25 | Vue.nextTick(() => {
26 | store.dispatch('errorLog/addErrorLog', {
27 | err,
28 | vm,
29 | info,
30 | url: window.location.href
31 | })
32 | console.error(err, info)
33 | })
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/utils/i18n.js:
--------------------------------------------------------------------------------
1 | // translate router.meta.title, be used in breadcrumb sidebar tagsview
2 | export function generateTitle(title) {
3 | const hasKey = this.$te('route.' + title)
4 |
5 | if (hasKey) {
6 | // $t :this method from vue-i18n, inject in @/lang/index.js
7 | const translatedTitle = this.$t('route.' + title)
8 |
9 | return translatedTitle
10 | }
11 | return title
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils/openWindow.js:
--------------------------------------------------------------------------------
1 | /**
2 | *Created by jiachenpan on 16/11/29.
3 | * @param {Sting} url
4 | * @param {Sting} title
5 | * @param {Number} w
6 | * @param {Number} h
7 | */
8 |
9 | export default function openWindow(url, title, w, h) {
10 | // Fixes dual-screen position Most browsers Firefox
11 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
12 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
13 |
14 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width
15 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height
16 |
17 | const left = ((width / 2) - (w / 2)) + dualScreenLeft
18 | const top = ((height / 2) - (h / 2)) + dualScreenTop
19 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left)
20 |
21 | // Puts focus on the newWindow
22 | if (window.focus) {
23 | newWindow.focus()
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/utils/permission.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 |
3 | /**
4 | * @param {Array} value
5 | * @returns {Boolean}
6 | * @example see @/views/permission/directive.vue
7 | */
8 | export default function checkPermission(value) {
9 | if (value && value instanceof Array && value.length > 0) {
10 | const roles = store.getters && store.getters.roles
11 | const permissionRoles = value
12 |
13 | const hasPermission = roles.some(role => {
14 | return permissionRoles.includes(role)
15 | })
16 |
17 | if (!hasPermission) {
18 | return false
19 | }
20 | return true
21 | } else {
22 | console.error(`need roles! Like v-permission="['admin','editor']"`)
23 | return false
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { Message, MessageBox } from 'element-ui'
3 | import store from '../store'
4 | import { getToken } from '@/utils/auth'
5 |
6 | // 创建axios实例
7 | const service = axios.create({
8 | baseURL: process.env.BASE_API, // api 的 base_url
9 | timeout: 5000 // 请求超时时间
10 | })
11 |
12 | // request拦截器
13 | service.interceptors.request.use(
14 | config => {
15 | if (store.getters.token) {
16 | config.headers['X-Token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
17 | }
18 | return config
19 | },
20 | error => {
21 | // Do something with request error
22 | console.log(error) // for debug
23 | Promise.reject(error)
24 | }
25 | )
26 |
27 | // response 拦截器
28 | service.interceptors.response.use(
29 | response => {
30 | /**
31 | * code为非20000是抛错 可结合自己业务进行修改
32 | */
33 | const res = response.data
34 | if (res.code !== 20000) {
35 | Message({
36 | message: res.message,
37 | type: 'error',
38 | duration: 5 * 1000
39 | })
40 |
41 | // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
42 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
43 | MessageBox.confirm(
44 | '你已被登出,可以取消继续留在该页面,或者重新登录',
45 | '确定登出',
46 | {
47 | confirmButtonText: '重新登录',
48 | cancelButtonText: '取消',
49 | type: 'warning'
50 | }
51 | ).then(() => {
52 | store.dispatch('FedLogOut').then(() => {
53 | location.reload() // 为了重新实例化vue-router对象 避免bug
54 | })
55 | })
56 | }
57 | return Promise.reject('error')
58 | } else {
59 | return response.data
60 | }
61 | },
62 | error => {
63 | console.log('err' + error) // for debug
64 | Message({
65 | message: error.message,
66 | type: 'error',
67 | duration: 5 * 1000
68 | })
69 | return Promise.reject(error)
70 | }
71 | )
72 |
73 | export default service
74 |
--------------------------------------------------------------------------------
/src/utils/scrollTo.js:
--------------------------------------------------------------------------------
1 | Math.easeInOutQuad = function(t, b, c, d) {
2 | t /= d / 2
3 | if (t < 1) {
4 | return c / 2 * t * t + b
5 | }
6 | t--
7 | return -c / 2 * (t * (t - 2) - 1) + b
8 | }
9 |
10 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
11 | var requestAnimFrame = (function() {
12 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
13 | })()
14 |
15 | // because it's so fucking difficult to detect the scrolling element, just move them all
16 | function move(amount) {
17 | document.documentElement.scrollTop = amount
18 | document.body.parentNode.scrollTop = amount
19 | document.body.scrollTop = amount
20 | }
21 |
22 | function position() {
23 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
24 | }
25 |
26 | export function scrollTo(to, duration, callback) {
27 | const start = position()
28 | const change = to - start
29 | const increment = 20
30 | let currentTime = 0
31 | duration = (typeof (duration) === 'undefined') ? 500 : duration
32 | var animateScroll = function() {
33 | // increment the time
34 | currentTime += increment
35 | // find the value with the quadratic in-out easing function
36 | var val = Math.easeInOutQuad(currentTime, start, change, duration)
37 | // move the document.body
38 | move(val)
39 | // do the animation unless its over
40 | if (currentTime < duration) {
41 | requestAnimFrame(animateScroll)
42 | } else {
43 | if (callback && typeof (callback) === 'function') {
44 | // the animation is done so lets callback
45 | callback()
46 | }
47 | }
48 | }
49 | animateScroll()
50 | }
51 |
--------------------------------------------------------------------------------
/src/utils/validate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jiachenpan on 16/11/18.
3 | */
4 |
5 | export function isExternal(path) {
6 | return /^(https?:|mailto:|tel:)/.test(path)
7 | }
8 |
--------------------------------------------------------------------------------
/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
OOPS!
12 |
15 |
{{ message }}
16 |
请检查您输入的网址是否正确,请点击以下按钮返回主页或者发送错误报告
17 |
返回首页
18 |
19 |
20 |
21 |
22 |
23 |
34 |
35 |
229 |
--------------------------------------------------------------------------------
/src/views/account/email.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 邮箱地址
10 |
11 |
12 |
13 |
14 |
15 | 提交
16 |
17 |
18 |
19 |
20 |
21 |
66 |
67 |
77 |
--------------------------------------------------------------------------------
/src/views/account/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 添加
11 |
12 |
13 |
22 |
23 |
24 | {{ scope.row.id }}
25 |
26 |
27 |
28 |
29 |
30 | {{ scope.row.name }}
31 |
32 |
33 |
34 |
35 |
36 | {{ scope.row.accessKey }}
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 | {{ scope.row.info }}
49 |
50 |
51 |
52 |
53 |
54 | {{ checkIsDelete(scope.row.isDelete) }}
55 |
56 |
57 |
58 |
59 |
60 | {{ scope.row.createTime | formatDate }}
61 |
62 |
63 |
64 |
65 |
66 | 查看
67 |
68 | 编辑
69 |
70 |
71 |
72 |
79 |
80 |
81 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
106 |
107 |
108 |
109 |
110 |
259 |
260 |
270 |
--------------------------------------------------------------------------------
/src/views/account/info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/views/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
32 |
33 |
34 |
35 |
36 |
51 |
69 |
--------------------------------------------------------------------------------
/src/views/layout/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
47 |
48 |
70 |
--------------------------------------------------------------------------------
/src/views/layout/components/AppMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
21 |
22 |
30 |
--------------------------------------------------------------------------------
/src/views/layout/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
![]()
8 |
9 |
10 |
11 |
12 |
13 | 主页
14 |
15 |
16 |
17 | 退出
18 |
19 |
20 |
21 |
22 |
23 |
24 |
52 |
53 |
95 |
96 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/Item.vue:
--------------------------------------------------------------------------------
1 |
31 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
37 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/SidebarItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
92 |
--------------------------------------------------------------------------------
/src/views/layout/components/Sidebar/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
16 |
17 |
40 |
--------------------------------------------------------------------------------
/src/views/layout/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navbar } from './Navbar'
2 | export { default as Sidebar } from './Sidebar'
3 | export { default as AppMain } from './AppMain'
4 |
--------------------------------------------------------------------------------
/src/views/layout/mixin/ResizeHandler.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 |
3 | const { body } = document
4 | const WIDTH = 992 // refer to Bootstrap's responsive design
5 |
6 | export default {
7 | watch: {
8 | $route(route) {
9 | if (this.device === 'mobile' && this.sidebar.opened) {
10 | store.dispatch('CloseSideBar', { withoutAnimation: false })
11 | }
12 | }
13 | },
14 | beforeMount() {
15 | window.addEventListener('resize', this.resizeHandler)
16 | },
17 | mounted() {
18 | const isMobile = this.isMobile()
19 | if (isMobile) {
20 | store.dispatch('ToggleDevice', 'mobile')
21 | store.dispatch('CloseSideBar', { withoutAnimation: true })
22 | }
23 | },
24 | methods: {
25 | isMobile() {
26 | const rect = body.getBoundingClientRect()
27 | return rect.width - 1 < WIDTH
28 | },
29 | resizeHandler() {
30 | if (!document.hidden) {
31 | const isMobile = this.isMobile()
32 | store.dispatch('ToggleDevice', isMobile ? 'mobile' : 'desktop')
33 |
34 | if (isMobile) {
35 | store.dispatch('CloseSideBar', { withoutAnimation: true })
36 | }
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/views/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 | 量化管理后台
12 |
13 |
14 |
15 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
40 |
41 | 登录
46 |
47 |
48 |
49 |
50 |
51 |
117 |
118 |
150 |
151 |
205 |
--------------------------------------------------------------------------------
/src/views/order/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {{ scope.row.orderType | formatOrderType }}
23 |
24 |
25 |
26 |
27 |
28 | {{ scope.row.createTime | formatDate }}
29 |
30 |
31 |
32 |
33 |
34 | {{ scope.row.finishedTime | formatDate }}
35 |
36 |
37 |
38 |
39 |
40 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/views/order/profit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {{ scope.row.divide | divideCad }}
20 |
21 |
22 |
23 |
24 |
25 | {{ scope.row.createTime | formatDate }}
26 |
27 |
28 |
29 |
30 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/views/robot/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
44 |
45 |
46 |
47 |
48 | {{title}}
49 | 取消
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/views/robot/info.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 机器人编号: {{ scope.row.robotId }}
9 | 日志信息: {{ scope.row.msg }}
10 |
11 |
12 | {{ scope.row.msg }}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | {{ scope.row.date }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/views/robot/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{ scope.row.robotName }}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 未启动
23 |
24 |
25 | 运行中
26 |
27 |
28 |
29 |
30 |
31 | 启动
37 | 停止
38 | 实时日志
39 | 订单
40 | 盈亏
46 | 修改
47 | 删除
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/views/strategy/list.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 添加新的策略
11 |
12 |
13 |
20 |
21 |
22 | {{ scope.row.id }}
23 |
24 |
25 |
26 |
27 |
28 | {{ scope.row.strategyName }}
29 |
30 |
31 |
32 |
33 |
34 | {{ scope.row.buyAllWeights }}
35 |
36 |
37 |
38 |
39 |
40 | {{ scope.row.sellAllWeights }}
41 |
42 |
43 |
44 |
45 |
46 | {{ checkIsLimitPrice(scope.row.isLimitPrice) }}
47 |
48 |
49 |
50 |
51 |
52 | {{ checkBuyState(scope.row.isAllBuy) }}
53 |
54 |
55 |
56 |
57 |
58 | {{ checkSellState(scope.row.isAllSell) }}
59 |
60 |
61 |
62 |
63 |
64 | {{ scope.row.sleep }}秒
65 |
66 |
67 |
68 |
69 |
70 | 修改策略
76 | 删除策略
77 |
78 |
79 |
80 |
81 |
82 |
83 |
208 |
209 |
219 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IndexOutOfBounds998/quant-admin/e894b3d33ee3abd2e9b2b946b7e1b5676bce16cb/static/.gitkeep
--------------------------------------------------------------------------------