初始化项目
Showing
42 changed files
with
1488 additions
and
0 deletions
.editorconfig
0 → 100644
.env.development
0 → 100644
.env.production
0 → 100644
.env.staging
0 → 100644
.eslintignore
0 → 100644
.eslintrc.js
0 → 100644
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': [ | ||
18 | 2, | ||
19 | { | ||
20 | singleline: 10, | ||
21 | multiline: { | ||
22 | max: 1, | ||
23 | allowFirstLine: false | ||
24 | } | ||
25 | } | ||
26 | ], | ||
27 | 'vue/singleline-html-element-content-newline': 'off', | ||
28 | 'vue/multiline-html-element-content-newline': 'off', | ||
29 | 'vue/name-property-casing': ['error', 'PascalCase'], | ||
30 | 'vue/no-v-html': 'off', | ||
31 | 'accessor-pairs': 2, | ||
32 | 'arrow-spacing': [ | ||
33 | 2, | ||
34 | { | ||
35 | before: true, | ||
36 | after: true | ||
37 | } | ||
38 | ], | ||
39 | 'block-spacing': [2, 'always'], | ||
40 | 'brace-style': [ | ||
41 | 2, | ||
42 | '1tbs', | ||
43 | { | ||
44 | allowSingleLine: true | ||
45 | } | ||
46 | ], | ||
47 | camelcase: [ | ||
48 | 0, | ||
49 | { | ||
50 | properties: 'always' | ||
51 | } | ||
52 | ], | ||
53 | 'comma-dangle': [2, 'never'], | ||
54 | 'comma-spacing': [ | ||
55 | 2, | ||
56 | { | ||
57 | before: false, | ||
58 | after: true | ||
59 | } | ||
60 | ], | ||
61 | 'comma-style': [2, 'last'], | ||
62 | 'constructor-super': 2, | ||
63 | curly: [2, 'multi-line'], | ||
64 | 'dot-location': [2, 'property'], | ||
65 | 'eol-last': 2, | ||
66 | eqeqeq: ['error', 'always', { null: 'ignore' }], | ||
67 | 'generator-star-spacing': [ | ||
68 | 2, | ||
69 | { | ||
70 | before: true, | ||
71 | after: true | ||
72 | } | ||
73 | ], | ||
74 | 'handle-callback-err': [2, '^(err|error)$'], | ||
75 | 'indent': ['off', 2], | ||
76 | 'jsx-quotes': [2, 'prefer-single'], | ||
77 | 'key-spacing': [ | ||
78 | 2, | ||
79 | { | ||
80 | beforeColon: false, | ||
81 | afterColon: true | ||
82 | } | ||
83 | ], | ||
84 | 'keyword-spacing': [ | ||
85 | 2, | ||
86 | { | ||
87 | before: true, | ||
88 | after: true | ||
89 | } | ||
90 | ], | ||
91 | 'new-cap': [ | ||
92 | 2, | ||
93 | { | ||
94 | newIsCap: true, | ||
95 | capIsNew: false | ||
96 | } | ||
97 | ], | ||
98 | 'new-parens': 2, | ||
99 | 'no-array-constructor': 2, | ||
100 | 'no-caller': 2, | ||
101 | 'no-console': 'off', | ||
102 | 'no-class-assign': 2, | ||
103 | 'no-cond-assign': 2, | ||
104 | 'no-const-assign': 2, | ||
105 | 'no-control-regex': 0, | ||
106 | 'no-delete-var': 2, | ||
107 | 'no-dupe-args': 2, | ||
108 | 'no-dupe-class-members': 2, | ||
109 | 'no-dupe-keys': 2, | ||
110 | 'no-duplicate-case': 2, | ||
111 | 'no-empty-character-class': 2, | ||
112 | 'no-empty-pattern': 2, | ||
113 | 'no-eval': 2, | ||
114 | 'no-ex-assign': 2, | ||
115 | 'no-extend-native': 2, | ||
116 | 'no-extra-bind': 2, | ||
117 | 'no-extra-boolean-cast': 2, | ||
118 | 'no-extra-parens': [2, 'functions'], | ||
119 | 'no-fallthrough': 2, | ||
120 | 'no-floating-decimal': 2, | ||
121 | 'no-func-assign': 2, | ||
122 | 'no-implied-eval': 2, | ||
123 | 'no-inner-declarations': [2, 'functions'], | ||
124 | 'no-invalid-regexp': 2, | ||
125 | 'no-irregular-whitespace': 2, | ||
126 | 'no-iterator': 2, | ||
127 | 'no-label-var': 2, | ||
128 | 'no-labels': [ | ||
129 | 2, | ||
130 | { | ||
131 | allowLoop: false, | ||
132 | allowSwitch: false | ||
133 | } | ||
134 | ], | ||
135 | 'no-lone-blocks': 2, | ||
136 | 'no-mixed-spaces-and-tabs': 2, | ||
137 | 'no-multi-spaces': 2, | ||
138 | 'no-multi-str': 2, | ||
139 | 'no-multiple-empty-lines': [ | ||
140 | 2, | ||
141 | { | ||
142 | max: 1 | ||
143 | } | ||
144 | ], | ||
145 | 'no-native-reassign': 2, | ||
146 | 'no-negated-in-lhs': 2, | ||
147 | 'no-new-object': 2, | ||
148 | 'no-new-require': 2, | ||
149 | 'no-new-symbol': 2, | ||
150 | 'no-new-wrappers': 2, | ||
151 | 'no-obj-calls': 2, | ||
152 | 'no-octal': 2, | ||
153 | 'no-octal-escape': 2, | ||
154 | 'no-path-concat': 2, | ||
155 | 'no-proto': 2, | ||
156 | 'no-redeclare': 2, | ||
157 | 'no-regex-spaces': 2, | ||
158 | 'no-return-assign': [2, 'except-parens'], | ||
159 | 'no-self-assign': 2, | ||
160 | 'no-self-compare': 2, | ||
161 | 'no-sequences': 2, | ||
162 | 'no-shadow-restricted-names': 2, | ||
163 | 'no-spaced-func': 2, | ||
164 | 'no-sparse-arrays': 2, | ||
165 | 'no-this-before-super': 2, | ||
166 | 'no-throw-literal': 2, | ||
167 | 'no-trailing-spaces': 2, | ||
168 | 'no-undef': 2, | ||
169 | 'no-undef-init': 2, | ||
170 | 'no-unexpected-multiline': 2, | ||
171 | 'no-unmodified-loop-condition': 2, | ||
172 | 'no-unneeded-ternary': [ | ||
173 | 2, | ||
174 | { | ||
175 | defaultAssignment: false | ||
176 | } | ||
177 | ], | ||
178 | 'no-unreachable': 2, | ||
179 | 'no-unsafe-finally': 2, | ||
180 | 'no-unused-vars': [ | ||
181 | 2, | ||
182 | { | ||
183 | vars: 'all', | ||
184 | args: 'none' | ||
185 | } | ||
186 | ], | ||
187 | 'no-useless-call': 2, | ||
188 | 'no-useless-computed-key': 2, | ||
189 | 'no-useless-constructor': 2, | ||
190 | 'no-useless-escape': 0, | ||
191 | 'no-whitespace-before-property': 2, | ||
192 | 'no-with': 2, | ||
193 | 'one-var': [ | ||
194 | 2, | ||
195 | { | ||
196 | initialized: 'never' | ||
197 | } | ||
198 | ], | ||
199 | 'operator-linebreak': [ | ||
200 | 2, | ||
201 | 'after', | ||
202 | { | ||
203 | overrides: { | ||
204 | '?': 'before', | ||
205 | ':': 'before' | ||
206 | } | ||
207 | } | ||
208 | ], | ||
209 | 'padded-blocks': [2, 'never'], | ||
210 | quotes: [ | ||
211 | 2, | ||
212 | 'single', | ||
213 | { | ||
214 | avoidEscape: true, | ||
215 | allowTemplateLiterals: true | ||
216 | } | ||
217 | ], | ||
218 | semi: [2, 'never'], | ||
219 | 'semi-spacing': [ | ||
220 | 2, | ||
221 | { | ||
222 | before: false, | ||
223 | after: true | ||
224 | } | ||
225 | ], | ||
226 | 'space-before-blocks': [2, 'always'], | ||
227 | 'space-before-function-paren': [2, 'never'], | ||
228 | 'space-in-parens': [2, 'never'], | ||
229 | 'space-infix-ops': 2, | ||
230 | 'space-unary-ops': [ | ||
231 | 2, | ||
232 | { | ||
233 | words: true, | ||
234 | nonwords: false | ||
235 | } | ||
236 | ], | ||
237 | 'spaced-comment': [ | ||
238 | 2, | ||
239 | 'always', | ||
240 | { | ||
241 | markers: [ | ||
242 | 'global', | ||
243 | 'globals', | ||
244 | 'eslint', | ||
245 | 'eslint-disable', | ||
246 | '*package', | ||
247 | '!', | ||
248 | ',' | ||
249 | ] | ||
250 | } | ||
251 | ], | ||
252 | 'template-curly-spacing': [2, 'never'], | ||
253 | 'use-isnan': 2, | ||
254 | 'valid-typeof': 2, | ||
255 | 'wrap-iife': [2, 'any'], | ||
256 | 'yield-star-spacing': [2, 'both'], | ||
257 | yoda: [2, 'never'], | ||
258 | 'prefer-const': 2, | ||
259 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, | ||
260 | 'object-curly-spacing': [ | ||
261 | 2, | ||
262 | 'always', | ||
263 | { | ||
264 | objectsInObjects: false | ||
265 | } | ||
266 | ], | ||
267 | 'array-bracket-spacing': [2, 'never'], | ||
268 | 'vue/html-self-closing': ["error",{ | ||
269 | "html": { | ||
270 | "void": "never", | ||
271 | "normal": "any", | ||
272 | "component": "any" | ||
273 | }, | ||
274 | "svg": "always", | ||
275 | "math": "always" | ||
276 | }] | ||
277 | } | ||
278 | } |
.gitignore
0 → 100644
.postcssrc.js
0 → 100644
.travis.yml
0 → 100644
LICENSE
0 → 100644
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. |
babel.config.js
0 → 100644
build/index.js
0 → 100644
1 | const { run } = require('runjs') | ||
2 | const chalk = require('chalk') | ||
3 | // const config = require('../vue.config.js') | ||
4 | const rawArgv = process.argv.slice(2) | ||
5 | const args = rawArgv.join(' ') | ||
6 | |||
7 | if (process.env.npm_config_preview || rawArgv.includes('--preview')) { | ||
8 | const report = rawArgv.includes('--report') | ||
9 | |||
10 | run(`vue-cli-service build ${args}`) | ||
11 | |||
12 | const port = 9018 | ||
13 | const publicPath = '/' | ||
14 | |||
15 | var connect = require('connect') | ||
16 | var serveStatic = require('serve-static') | ||
17 | const app = connect() | ||
18 | |||
19 | app.use( | ||
20 | publicPath, | ||
21 | serveStatic('./dist', { | ||
22 | index: ['index.html', '/'] | ||
23 | }) | ||
24 | ) | ||
25 | |||
26 | app.listen(port, function () { | ||
27 | console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) | ||
28 | if (report) { | ||
29 | console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) | ||
30 | } | ||
31 | |||
32 | }) | ||
33 | } else { | ||
34 | run(`vue-cli-service build ${args}`) | ||
35 | } |
jest.config.js
0 → 100644
1 | module.exports = { | ||
2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], | ||
3 | transform: { | ||
4 | '^.+\\.vue$': 'vue-jest', | ||
5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': | ||
6 | 'jest-transform-stub', | ||
7 | '^.+\\.jsx?$': 'babel-jest' | ||
8 | }, | ||
9 | moduleNameMapper: { | ||
10 | '^@/(.*)$': '<rootDir>/src/$1' | ||
11 | }, | ||
12 | snapshotSerializers: ['jest-serializer-vue'], | ||
13 | testMatch: [ | ||
14 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' | ||
15 | ], | ||
16 | collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], | ||
17 | coverageDirectory: '<rootDir>/tests/unit/coverage', | ||
18 | // 'collectCoverage': true, | ||
19 | 'coverageReporters': [ | ||
20 | 'lcov', | ||
21 | 'text-summary' | ||
22 | ], | ||
23 | testURL: 'http://localhost/' | ||
24 | } |
package.json
0 → 100644
1 | { | ||
2 | "name": "vue-admin-template", | ||
3 | "version": "4.1.0", | ||
4 | "description": "A vue admin template with Element UI & axios & iconfont & permission control & lint", | ||
5 | "author": "Pan <panfree23@gmail.com>", | ||
6 | "license": "MIT", | ||
7 | "scripts": { | ||
8 | "dev": "vue-cli-service serve", | ||
9 | "build:prod": "vue-cli-service build", | ||
10 | "build:stage": "vue-cli-service build --mode staging", | ||
11 | "preview": "node build/index.js --preview", | ||
12 | "lint": "eslint --ext .js,.vue src", | ||
13 | "test:unit": "jest --clearCache && vue-cli-service test:unit", | ||
14 | "test:ci": "npm run lint && npm run test:unit", | ||
15 | "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" | ||
16 | }, | ||
17 | "dependencies": { | ||
18 | "axios": "0.18.0", | ||
19 | "lib-flexible": "^0.3.2", | ||
20 | "normalize.css": "7.0.0", | ||
21 | "vant": "^1.6.19", | ||
22 | "vue": "2.6.10", | ||
23 | "vue-router": "3.0.6", | ||
24 | "vuex": "3.1.0" | ||
25 | }, | ||
26 | "devDependencies": { | ||
27 | "@babel/core": "7.0.0", | ||
28 | "@babel/register": "7.0.0", | ||
29 | "@vue/cli-plugin-babel": "3.6.0", | ||
30 | "@vue/cli-plugin-eslint": "3.6.0", | ||
31 | "@vue/cli-plugin-unit-jest": "3.6.3", | ||
32 | "@vue/cli-service": "3.6.0", | ||
33 | "@vue/test-utils": "1.0.0-beta.29", | ||
34 | "babel-core": "7.0.0-bridge.0", | ||
35 | "babel-eslint": "10.0.1", | ||
36 | "babel-jest": "23.6.0", | ||
37 | "babel-plugin-import": "^1.11.2", | ||
38 | "chalk": "2.4.2", | ||
39 | "connect": "3.6.6", | ||
40 | "eslint": "5.15.3", | ||
41 | "eslint-plugin-vue": "5.2.2", | ||
42 | "html-webpack-plugin": "3.2.0", | ||
43 | "node-sass": "^4.9.0", | ||
44 | "postcss-pxtorem": "^4.0.1", | ||
45 | "runjs": "^4.3.2", | ||
46 | "sass-loader": "^7.1.0", | ||
47 | "script-ext-html-webpack-plugin": "2.1.3", | ||
48 | "script-loader": "0.7.2", | ||
49 | "serve-static": "^1.13.2", | ||
50 | "svgo": "1.2.2", | ||
51 | "vue-template-compiler": "2.6.10" | ||
52 | }, | ||
53 | "engines": { | ||
54 | "node": ">=8.9", | ||
55 | "npm": ">= 3.0.0" | ||
56 | }, | ||
57 | "browserslist": [ | ||
58 | "> 1%", | ||
59 | "last 2 versions", | ||
60 | "not ie <= 8" | ||
61 | ] | ||
62 | } |
public/favicon.ico
0 → 100644
No preview for this file type
public/index.html
0 → 100644
1 | <!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> | ||
7 | |||
8 | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||
9 | <title><%= webpackConfig.name %></title> | ||
10 | </head> | ||
11 | <body> | ||
12 | <noscript> | ||
13 | <strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | ||
14 | </noscript> | ||
15 | <div id="app"></div> | ||
16 | <!-- built files will be auto injected --> | ||
17 | </body> | ||
18 | </html> |
src/App.vue
0 → 100644
src/api/user.js
0 → 100644
src/assets/404_images/404.png
0 → 100644

95.8 KB
src/assets/404_images/404_cloud.png
0 → 100644

4.65 KB
src/assets/css/element-ui.scss
0 → 100644
1 | // cover some element-ui styles | ||
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 | |||
19 | // to fixed https://github.com/ElemeFE/element/issues/2461 | ||
20 | .el-dialog { | ||
21 | transform: none; | ||
22 | left: 0; | ||
23 | position: relative; | ||
24 | margin: 0 auto; | ||
25 | } | ||
26 | |||
27 | // refine element ui upload | ||
28 | .upload-container { | ||
29 | .el-upload { | ||
30 | width: 100%; | ||
31 | |||
32 | .el-upload-dragger { | ||
33 | width: 100%; | ||
34 | height: 200px; | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | // dropdown | ||
40 | .el-dropdown-menu { | ||
41 | a { | ||
42 | display: block | ||
43 | } | ||
44 | } |
src/assets/css/index.scss
0 → 100644
1 | @import './variables.scss'; | ||
2 | @import './mixin.scss'; | ||
3 | @import './transition.scss'; | ||
4 | @import './element-ui.scss'; | ||
5 | @import './sidebar.scss'; | ||
6 | |||
7 | body { | ||
8 | height: 100%; | ||
9 | -moz-osx-font-smoothing: grayscale; | ||
10 | -webkit-font-smoothing: antialiased; | ||
11 | text-rendering: optimizeLegibility; | ||
12 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; | ||
13 | } | ||
14 | |||
15 | label { | ||
16 | font-weight: 700; | ||
17 | } | ||
18 | |||
19 | html { | ||
20 | height: 100%; | ||
21 | box-sizing: border-box; | ||
22 | } | ||
23 | |||
24 | #app { | ||
25 | height: 100%; | ||
26 | } | ||
27 | |||
28 | *, | ||
29 | *:before, | ||
30 | *:after { | ||
31 | box-sizing: inherit; | ||
32 | } | ||
33 | |||
34 | a:focus, | ||
35 | a:active { | ||
36 | outline: none; | ||
37 | } | ||
38 | |||
39 | a, | ||
40 | a:focus, | ||
41 | a:hover { | ||
42 | cursor: pointer; | ||
43 | color: inherit; | ||
44 | text-decoration: none; | ||
45 | } | ||
46 | |||
47 | div:focus { | ||
48 | outline: none; | ||
49 | } | ||
50 | |||
51 | .clearfix { | ||
52 | &:after { | ||
53 | visibility: hidden; | ||
54 | display: block; | ||
55 | font-size: 0; | ||
56 | content: " "; | ||
57 | clear: both; | ||
58 | height: 0; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | // main-container global css | ||
63 | .app-container { | ||
64 | padding: 20px; | ||
65 | } |
src/assets/css/mixin.scss
0 → 100644
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 | } |
src/assets/css/sidebar.scss
0 → 100644
1 | #app { | ||
2 | |||
3 | .main-container { | ||
4 | min-height: 100%; | ||
5 | transition: margin-left .28s; | ||
6 | margin-left: $sideBarWidth; | ||
7 | position: relative; | ||
8 | } | ||
9 | |||
10 | .sidebar-container { | ||
11 | transition: width 0.28s; | ||
12 | width: $sideBarWidth !important; | ||
13 | background-color: $menuBg; | ||
14 | height: 100%; | ||
15 | position: fixed; | ||
16 | font-size: 0px; | ||
17 | top: 0; | ||
18 | bottom: 0; | ||
19 | left: 0; | ||
20 | z-index: 1001; | ||
21 | overflow: hidden; | ||
22 | |||
23 | // reset element-ui css | ||
24 | .horizontal-collapse-transition { | ||
25 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; | ||
26 | } | ||
27 | |||
28 | .scrollbar-wrapper { | ||
29 | overflow-x: hidden !important; | ||
30 | } | ||
31 | |||
32 | .el-scrollbar__bar.is-vertical { | ||
33 | right: 0px; | ||
34 | } | ||
35 | |||
36 | .el-scrollbar { | ||
37 | height: 100%; | ||
38 | } | ||
39 | |||
40 | &.has-logo { | ||
41 | .el-scrollbar { | ||
42 | height: calc(100% - 50px); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | .is-horizontal { | ||
47 | display: none; | ||
48 | } | ||
49 | |||
50 | a { | ||
51 | display: inline-block; | ||
52 | width: 100%; | ||
53 | overflow: hidden; | ||
54 | } | ||
55 | |||
56 | .svg-icon { | ||
57 | margin-right: 16px; | ||
58 | } | ||
59 | |||
60 | .el-menu { | ||
61 | border: none; | ||
62 | height: 100%; | ||
63 | width: 100% !important; | ||
64 | } | ||
65 | |||
66 | // menu hover | ||
67 | .submenu-title-noDropdown, | ||
68 | .el-submenu__title { | ||
69 | &:hover { | ||
70 | background-color: $menuHover !important; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | .is-active>.el-submenu__title { | ||
75 | color: $subMenuActiveText !important; | ||
76 | } | ||
77 | |||
78 | & .nest-menu .el-submenu>.el-submenu__title, | ||
79 | & .el-submenu .el-menu-item { | ||
80 | min-width: $sideBarWidth !important; | ||
81 | background-color: $subMenuBg !important; | ||
82 | |||
83 | &:hover { | ||
84 | background-color: $subMenuHover !important; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | .hideSidebar { | ||
90 | .sidebar-container { | ||
91 | width: 54px !important; | ||
92 | } | ||
93 | |||
94 | .main-container { | ||
95 | margin-left: 54px; | ||
96 | } | ||
97 | |||
98 | .submenu-title-noDropdown { | ||
99 | padding: 0 !important; | ||
100 | position: relative; | ||
101 | |||
102 | .el-tooltip { | ||
103 | padding: 0 !important; | ||
104 | |||
105 | .svg-icon { | ||
106 | margin-left: 20px; | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | .el-submenu { | ||
112 | overflow: hidden; | ||
113 | |||
114 | &>.el-submenu__title { | ||
115 | padding: 0 !important; | ||
116 | |||
117 | .svg-icon { | ||
118 | margin-left: 20px; | ||
119 | } | ||
120 | |||
121 | .el-submenu__icon-arrow { | ||
122 | display: none; | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | .el-menu--collapse { | ||
128 | .el-submenu { | ||
129 | &>.el-submenu__title { | ||
130 | &>span { | ||
131 | height: 0; | ||
132 | width: 0; | ||
133 | overflow: hidden; | ||
134 | visibility: hidden; | ||
135 | display: inline-block; | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | .el-menu--collapse .el-menu .el-submenu { | ||
143 | min-width: $sideBarWidth !important; | ||
144 | } | ||
145 | |||
146 | // mobile responsive | ||
147 | .mobile { | ||
148 | .main-container { | ||
149 | margin-left: 0px; | ||
150 | } | ||
151 | |||
152 | .sidebar-container { | ||
153 | transition: transform .28s; | ||
154 | width: $sideBarWidth !important; | ||
155 | } | ||
156 | |||
157 | &.hideSidebar { | ||
158 | .sidebar-container { | ||
159 | pointer-events: none; | ||
160 | transition-duration: 0.3s; | ||
161 | transform: translate3d(-$sideBarWidth, 0, 0); | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | .withoutAnimation { | ||
167 | |||
168 | .main-container, | ||
169 | .sidebar-container { | ||
170 | transition: none; | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | |||
175 | // when menu collapsed | ||
176 | .el-menu--vertical { | ||
177 | &>.el-menu { | ||
178 | .svg-icon { | ||
179 | margin-right: 16px; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | .nest-menu .el-submenu>.el-submenu__title, | ||
184 | .el-menu-item { | ||
185 | &:hover { | ||
186 | // you can use $subMenuHover | ||
187 | background-color: $menuHover !important; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | // the scroll bar appears when the subMenu is too long | ||
192 | >.el-menu--popup { | ||
193 | max-height: 100vh; | ||
194 | overflow-y: auto; | ||
195 | |||
196 | &::-webkit-scrollbar-track-piece { | ||
197 | background: #d3dce6; | ||
198 | } | ||
199 | |||
200 | &::-webkit-scrollbar { | ||
201 | width: 6px; | ||
202 | } | ||
203 | |||
204 | &::-webkit-scrollbar-thumb { | ||
205 | background: #99a9bf; | ||
206 | border-radius: 20px; | ||
207 | } | ||
208 | } | ||
209 | } |
src/assets/css/transition.scss
0 → 100644
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 | } |
src/assets/css/variables.scss
0 → 100644
1 | // sidebar | ||
2 | $menuText:#bfcbd9; | ||
3 | $menuActiveText:#409EFF; | ||
4 | $subMenuActiveText:#f4f4f5; //https://github.com/ElemeFE/element/issues/12951 | ||
5 | |||
6 | $menuBg:#304156; | ||
7 | $menuHover:#263445; | ||
8 | |||
9 | $subMenuBg:#1f2d3d; | ||
10 | $subMenuHover:#001528; | ||
11 | |||
12 | $sideBarWidth: 210px; | ||
13 | |||
14 | // the :export directive is the magic sauce for webpack | ||
15 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass | ||
16 | :export { | ||
17 | menuText: $menuText; | ||
18 | menuActiveText: $menuActiveText; | ||
19 | subMenuActiveText: $subMenuActiveText; | ||
20 | menuBg: $menuBg; | ||
21 | menuHover: $menuHover; | ||
22 | subMenuBg: $subMenuBg; | ||
23 | subMenuHover: $subMenuHover; | ||
24 | sideBarWidth: $sideBarWidth; | ||
25 | } |
src/config/env.development.js
0 → 100644
src/config/env.production.js
0 → 100644
src/config/env.staging.js
0 → 100644
src/config/index.js
0 → 100644
src/main.js
0 → 100644
1 | import Vue from 'vue' | ||
2 | |||
3 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets | ||
4 | import '@/assets/css/index.scss' // global css | ||
5 | //移动端适配 | ||
6 | import "lib-flexible/flexible.js" | ||
7 | import App from './App' | ||
8 | import store from './store' | ||
9 | import router from './router' | ||
10 | Vue.config.productionTip = false | ||
11 | |||
12 | new Vue({ | ||
13 | el: '#app', | ||
14 | router, | ||
15 | store, | ||
16 | render: h => h(App) | ||
17 | }) |
src/router/index.js
0 → 100644
1 | import Vue from 'vue' | ||
2 | import Router from 'vue-router' | ||
3 | |||
4 | Vue.use(Router) | ||
5 | export const constantRoutes = [ | ||
6 | { | ||
7 | path: '/', | ||
8 | component: () => import('@/views/home/index'), | ||
9 | meta: { | ||
10 | keepAlive: false | ||
11 | } | ||
12 | } | ||
13 | ] | ||
14 | |||
15 | const createRouter = () => | ||
16 | new Router({ | ||
17 | // mode: 'history', // require service support | ||
18 | scrollBehavior: () => ({ y: 0 }), | ||
19 | routes: constantRoutes | ||
20 | }) | ||
21 | |||
22 | export default createRouter() |
src/store/getters.js
0 → 100644
src/store/index.js
0 → 100644
src/store/modules/app.js
0 → 100644
src/utils/index.js
0 → 100644
1 | /** | ||
2 | * Created by PanJiaChen on 16/11/18. | ||
3 | */ | ||
4 | |||
5 | /** | ||
6 | * Parse the time to string | ||
7 | * @param {(Object|string|number)} time | ||
8 | * @param {string} cFormat | ||
9 | * @returns {string} | ||
10 | */ | ||
11 | export function parseTime(time, cFormat) { | ||
12 | if (arguments.length === 0) { | ||
13 | return null | ||
14 | } | ||
15 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' | ||
16 | let date | ||
17 | if (typeof time === 'object') { | ||
18 | date = time | ||
19 | } else { | ||
20 | if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { | ||
21 | time = parseInt(time) | ||
22 | } | ||
23 | if ((typeof time === 'number') && (time.toString().length === 10)) { | ||
24 | time = time * 1000 | ||
25 | } | ||
26 | date = new Date(time) | ||
27 | } | ||
28 | const formatObj = { | ||
29 | y: date.getFullYear(), | ||
30 | m: date.getMonth() + 1, | ||
31 | d: date.getDate(), | ||
32 | h: date.getHours(), | ||
33 | i: date.getMinutes(), | ||
34 | s: date.getSeconds(), | ||
35 | a: date.getDay() | ||
36 | } | ||
37 | const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { | ||
38 | let value = formatObj[key] | ||
39 | // Note: getDay() returns 0 on Sunday | ||
40 | if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] } | ||
41 | if (result.length > 0 && value < 10) { | ||
42 | value = '0' + value | ||
43 | } | ||
44 | return value || 0 | ||
45 | }) | ||
46 | return time_str | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * @param {number} time | ||
51 | * @param {string} option | ||
52 | * @returns {string} | ||
53 | */ | ||
54 | export function formatTime(time, option) { | ||
55 | if (('' + time).length === 10) { | ||
56 | time = parseInt(time) * 1000 | ||
57 | } else { | ||
58 | time = +time | ||
59 | } | ||
60 | const d = new Date(time) | ||
61 | const now = Date.now() | ||
62 | |||
63 | const diff = (now - d) / 1000 | ||
64 | |||
65 | if (diff < 30) { | ||
66 | return '刚刚' | ||
67 | } else if (diff < 3600) { | ||
68 | // less 1 hour | ||
69 | return Math.ceil(diff / 60) + '分钟前' | ||
70 | } else if (diff < 3600 * 24) { | ||
71 | return Math.ceil(diff / 3600) + '小时前' | ||
72 | } else if (diff < 3600 * 24 * 2) { | ||
73 | return '1天前' | ||
74 | } | ||
75 | if (option) { | ||
76 | return parseTime(time, option) | ||
77 | } else { | ||
78 | return ( | ||
79 | d.getMonth() + | ||
80 | 1 + | ||
81 | '月' + | ||
82 | d.getDate() + | ||
83 | '日' + | ||
84 | d.getHours() + | ||
85 | '时' + | ||
86 | d.getMinutes() + | ||
87 | '分' | ||
88 | ) | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * @param {string} url | ||
94 | * @returns {Object} | ||
95 | */ | ||
96 | export function param2Obj(url) { | ||
97 | const search = url.split('?')[1] | ||
98 | if (!search) { | ||
99 | return {} | ||
100 | } | ||
101 | return JSON.parse( | ||
102 | '{"' + | ||
103 | decodeURIComponent(search) | ||
104 | .replace(/"/g, '\\"') | ||
105 | .replace(/&/g, '","') | ||
106 | .replace(/=/g, '":"') | ||
107 | .replace(/\+/g, ' ') + | ||
108 | '"}' | ||
109 | ) | ||
110 | } |
src/utils/request.1.js
0 → 100644
1 | import axios from 'axios' | ||
2 | import store from '@/store' | ||
3 | |||
4 | // create an axios instance | ||
5 | const service = axios.create({ | ||
6 | baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url | ||
7 | withCredentials: true, // send cookies when cross-domain requests | ||
8 | timeout: 5000 // request timeout | ||
9 | }) | ||
10 | |||
11 | // request interceptor | ||
12 | service.interceptors.request.use( | ||
13 | config => { | ||
14 | // do something before request is sent | ||
15 | if (store.getters.token) { | ||
16 | // let each request carry token | ||
17 | // ['X-Token'] is a custom headers key | ||
18 | // please modify it according to the actual situation | ||
19 | config.headers['X-Token'] = '' | ||
20 | } | ||
21 | return config | ||
22 | }, | ||
23 | error => { | ||
24 | // do something with request error | ||
25 | console.log(error) // for debug | ||
26 | return Promise.reject(error) | ||
27 | } | ||
28 | ) | ||
29 | |||
30 | // response interceptor | ||
31 | service.interceptors.response.use( | ||
32 | response => { | ||
33 | const res = response.data | ||
34 | |||
35 | // if the custom code is not 20000, it is judged as an error. | ||
36 | if (res.code !== 20000) { | ||
37 | Message({ | ||
38 | message: res.message || 'error', | ||
39 | type: 'error', | ||
40 | duration: 5 * 1000 | ||
41 | }) | ||
42 | |||
43 | // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired; | ||
44 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) { | ||
45 | // to re-login | ||
46 | MessageBox.confirm( | ||
47 | 'You have been logged out, you can cancel to stay on this page, or log in again', | ||
48 | 'Confirm logout', | ||
49 | { | ||
50 | confirmButtonText: 'Re-Login', | ||
51 | cancelButtonText: 'Cancel', | ||
52 | type: 'warning' | ||
53 | } | ||
54 | ).then(() => { | ||
55 | store.dispatch('user/resetToken').then(() => { | ||
56 | location.reload() | ||
57 | }) | ||
58 | }) | ||
59 | } | ||
60 | return Promise.reject(res.message || 'error') | ||
61 | } else { | ||
62 | return res | ||
63 | } | ||
64 | }, | ||
65 | error => { | ||
66 | console.log('err' + error) // for debug | ||
67 | Message({ | ||
68 | message: error.message, | ||
69 | type: 'error', | ||
70 | duration: 5 * 1000 | ||
71 | }) | ||
72 | return Promise.reject(error) | ||
73 | } | ||
74 | ) | ||
75 | |||
76 | export default service |
src/utils/request.js
0 → 100644
1 | import axios from 'axios' | ||
2 | import store from '@/store' | ||
3 | import { Toast } from 'vant' | ||
4 | // create an axios instance | ||
5 | const service = axios.create({ | ||
6 | baseURL: process.env.BASE_URL, // url = base url + request url | ||
7 | withCredentials: true, // send cookies when cross-domain requests | ||
8 | timeout: 5000 // request timeout | ||
9 | }) | ||
10 | |||
11 | // request拦截器 request interceptor | ||
12 | service.interceptors.request.use( | ||
13 | config => { | ||
14 | // 不传递默认开启loading | ||
15 | if (!config.hideloading) { | ||
16 | // loading | ||
17 | Toast.loading({ | ||
18 | forbidClick: true | ||
19 | }) | ||
20 | } | ||
21 | if (store.getters.token) { | ||
22 | config.headers['X-Token'] = '' | ||
23 | } | ||
24 | return config | ||
25 | }, | ||
26 | error => { | ||
27 | // do something with request error | ||
28 | console.log(error) // for debug | ||
29 | return Promise.reject(error) | ||
30 | } | ||
31 | ) | ||
32 | // respone拦截器 | ||
33 | service.interceptors.response.use( | ||
34 | response => { | ||
35 | Toast.clear() | ||
36 | // 如果是数据流 | ||
37 | if (response.config.responseType === 'arraybuffer') { | ||
38 | return response.data | ||
39 | } else { | ||
40 | const res = response.data | ||
41 | if (res.status !== 200) { | ||
42 | // 登录超时,重新登录 | ||
43 | if (res.status === 401) { | ||
44 | store.dispatch('FedLogOut').then(() => { | ||
45 | location.reload() | ||
46 | }) | ||
47 | } | ||
48 | return Promise.reject(res || 'error') | ||
49 | } else { | ||
50 | return Promise.resolve(res) | ||
51 | } | ||
52 | } | ||
53 | }, | ||
54 | error => { | ||
55 | Toast.clear() | ||
56 | console.log('err' + error) // for debug | ||
57 | return Promise.reject(error) | ||
58 | } | ||
59 | ) | ||
60 | |||
61 | export default service |
src/utils/signature.js
0 → 100644
File mode changed
src/utils/validate.js
0 → 100644
1 | /** | ||
2 | * Created by PanJiaChen on 16/11/18. | ||
3 | */ | ||
4 | |||
5 | /** | ||
6 | * @param {string} path | ||
7 | * @returns {Boolean} | ||
8 | */ | ||
9 | export function isExternal(path) { | ||
10 | return /^(https?:|mailto:|tel:)/.test(path) | ||
11 | } | ||
12 | |||
13 | /** | ||
14 | * @param {string} str | ||
15 | * @returns {Boolean} | ||
16 | */ | ||
17 | export function validUsername(str) { | ||
18 | const valid_map = ['admin', 'editor'] | ||
19 | return valid_map.indexOf(str.trim()) >= 0 | ||
20 | } |
src/views/home/index.vue
0 → 100644
1 | <!-- home --> | ||
2 | <template> | ||
3 | <div> | ||
4 | <h1>home</h1> | ||
5 | <van-button type="primary">主要按钮</van-button> | ||
6 | </div> | ||
7 | </template> | ||
8 | |||
9 | <script> | ||
10 | import { | ||
11 | Button | ||
12 | } from 'vant' | ||
13 | export default { | ||
14 | data() { | ||
15 | return {}; | ||
16 | }, | ||
17 | |||
18 | components: { | ||
19 | 'van-button': Button | ||
20 | }, | ||
21 | |||
22 | computed: {}, | ||
23 | |||
24 | mounted() {}, | ||
25 | |||
26 | methods: {} | ||
27 | } | ||
28 | |||
29 | </script> | ||
30 | <style lang='scss' scoped> | ||
31 | h1 { | ||
32 | background: red; | ||
33 | width: 375px; | ||
34 | } | ||
35 | |||
36 | </style> |
vue.config.js
0 → 100644
1 | 'use strict' | ||
2 | const path = require('path') | ||
3 | const defaultSettings = require('./src/config/index.js') | ||
4 | function resolve(dir) { | ||
5 | return path.join(__dirname, dir) | ||
6 | } | ||
7 | |||
8 | const name = defaultSettings.title || 'vue mobile template' // page title | ||
9 | const port = 9018 // dev port | ||
10 | module.exports = { | ||
11 | publicPath: '/', | ||
12 | outputDir: 'dist', | ||
13 | assetsDir: 'static', | ||
14 | lintOnSave: process.env.NODE_ENV === 'development', | ||
15 | productionSourceMap: false, | ||
16 | devServer: { | ||
17 | port: port, | ||
18 | open: true, | ||
19 | overlay: { | ||
20 | warnings: false, | ||
21 | errors: true | ||
22 | } | ||
23 | }, | ||
24 | // css: { | ||
25 | // loaderOptions: { | ||
26 | // postcss: { | ||
27 | // plugins: [ | ||
28 | // require('postcss-plugin-px2rem')({ | ||
29 | // rootValue: 75, // 换算基数, 默认100 ,这样的话把根标签的字体规定为1rem为50px,这样就可以从设计稿上量出多少个px直接在代码中写多上px了。 | ||
30 | // // unitPrecision: 5, //允许REM单位增长到的十进制数字。 | ||
31 | // // propWhiteList: [], //默认值是一个空数组,这意味着禁用白名单并启用所有属性。 | ||
32 | // // propBlackList: [], //黑名单 | ||
33 | // exclude: /(node_module)/, // 默认false,可以(reg)利用正则表达式排除某些文件夹的方法,例如/(node_module)\/如果想把前端UI框架内的px也转换成rem,请把此属性设为默认值 | ||
34 | // // selectorBlackList: [], //要忽略并保留为px的选择器 | ||
35 | // // ignoreIdentifier: false, //(boolean/string)忽略单个属性的方法,启用ignoreidentifier后,replace将自动设置为true。 | ||
36 | // // replace: true, // (布尔值)替换包含REM的规则,而不是添加回退。 | ||
37 | // mediaQuery: false, // (布尔值)允许在媒体查询中转换px。 | ||
38 | // minPixelValue: 3 // 设置要替换的最小像素值(3px会被转rem)。 默认 0 | ||
39 | // }) | ||
40 | // ] | ||
41 | // } | ||
42 | // } | ||
43 | // }, | ||
44 | configureWebpack: { | ||
45 | name: name, | ||
46 | resolve: { | ||
47 | alias: { | ||
48 | '@': resolve('src') | ||
49 | } | ||
50 | } | ||
51 | }, | ||
52 | chainWebpack(config) { | ||
53 | config.plugins.delete('preload') // TODO: need test | ||
54 | config.plugins.delete('prefetch') // TODO: need test | ||
55 | // set preserveWhitespace | ||
56 | config.module | ||
57 | .rule('vue') | ||
58 | .use('vue-loader') | ||
59 | .loader('vue-loader') | ||
60 | .tap(options => { | ||
61 | options.compilerOptions.preserveWhitespace = true | ||
62 | return options | ||
63 | }) | ||
64 | .end() | ||
65 | |||
66 | config | ||
67 | // https://webpack.js.org/configuration/devtool/#development | ||
68 | .when(process.env.NODE_ENV === 'development', config => | ||
69 | config.devtool('cheap-source-map') | ||
70 | ) | ||
71 | |||
72 | config.when(process.env.NODE_ENV !== 'development', config => { | ||
73 | console.log(config) | ||
74 | config | ||
75 | .plugin('ScriptExtHtmlWebpackPlugin') | ||
76 | .after('html') | ||
77 | .use('script-ext-html-webpack-plugin', [ | ||
78 | { | ||
79 | // `runtime` must same as runtimeChunk name. default is `runtime` | ||
80 | inline: /runtime\..*\.js$/ | ||
81 | } | ||
82 | ]) | ||
83 | .end() | ||
84 | config.optimization.splitChunks({ | ||
85 | chunks: 'all', | ||
86 | cacheGroups: { | ||
87 | libs: { | ||
88 | name: 'chunk-libs', | ||
89 | test: /[\\/]node_modules[\\/]/, | ||
90 | priority: 10, | ||
91 | chunks: 'initial' // only package third parties that are initially dependent | ||
92 | }, | ||
93 | // elementUI: { | ||
94 | // name: 'chunk-elementUI', // split elementUI into a single package | ||
95 | // priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app | ||
96 | // test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm | ||
97 | // }, | ||
98 | commons: { | ||
99 | name: 'chunk-commons', | ||
100 | test: resolve('src/components'), // can customize your rules | ||
101 | minChunks: 3, // minimum common number | ||
102 | priority: 5, | ||
103 | reuseExistingChunk: true | ||
104 | } | ||
105 | } | ||
106 | }) | ||
107 | config.optimization.runtimeChunk('single') | ||
108 | }) | ||
109 | } | ||
110 | } |
-
Please register or sign in to post a comment