Compare commits
317 Commits
webpack3
...
check-engi
Author | SHA1 | Date | |
---|---|---|---|
|
1dc7b64e3e | ||
|
896b7369bf | ||
|
a9b4467472 | ||
|
74b6bb8069 | ||
|
ec7c585813 | ||
|
d919acf64b | ||
|
3594052c90 | ||
|
d1b786946c | ||
|
59b1493579 | ||
|
63dba8b7d8 | ||
|
0c6e45e9a1 | ||
|
e1d61898ed | ||
|
fef9fda685 | ||
|
0c50029cf7 | ||
|
7c33568883 | ||
|
d927d4172f | ||
|
ccb227fbec | ||
|
fd4b5c4052 | ||
|
a2b9b37c9c | ||
|
9665307220 | ||
|
27322ac4d3 | ||
|
715fe3e684 | ||
|
abb1d91a9e | ||
|
d1f32a05a8 | ||
|
6c6f03f664 | ||
|
8b382e1e15 | ||
|
e601da38d0 | ||
|
c93d88a348 | ||
|
686d0acd6d | ||
|
05ca5cae6d | ||
|
f3aefe54e8 | ||
|
a80999e0a2 | ||
|
4b5b0a6e6b | ||
|
3d973f7ddd | ||
|
b44335f012 | ||
|
f175427a61 | ||
|
b8d47bd847 | ||
|
8d242a002f | ||
|
763b31d915 | ||
|
183249a5d4 | ||
|
87272fd62d | ||
|
c963f56686 | ||
|
17f0d84b35 | ||
|
b0e3dc8617 | ||
|
e855f6a121 | ||
|
f38d5810d9 | ||
|
537ecf9990 | ||
|
dedf8e7ced | ||
|
6c1c03c828 | ||
|
20fdb5cd8b | ||
|
60b4549bf5 | ||
|
6b74a4ec72 | ||
|
1d3683a8c8 | ||
|
cf48ed218b | ||
|
9574643e92 | ||
|
8edf209498 | ||
|
66e8ca9cec | ||
|
653de1f31e | ||
|
1dfea3b287 | ||
|
698df4942d | ||
|
2ac4cee805 | ||
|
1e2fc52ac9 | ||
|
94a3d7be7e | ||
|
3560fcacf9 | ||
|
8e0b5624a9 | ||
|
44e4d3d005 | ||
|
fef2853951 | ||
|
f8f7667ab1 | ||
|
3e92814f13 | ||
|
5ca6f79836 | ||
|
dc6030bce6 | ||
|
5508f05132 | ||
|
cbc77c9a93 | ||
|
583e96cca5 | ||
|
6d9ac37662 | ||
|
9a5827ca8a | ||
|
c44d001df6 | ||
|
8029c076b9 | ||
|
d45e80549f | ||
|
0bc681efe6 | ||
|
0f3d2bcd05 | ||
|
2d8a238880 | ||
|
30d4d85b34 | ||
|
5224a00ddc | ||
|
054e9c4a73 | ||
|
041075633f | ||
|
d955fe8b0f | ||
|
e0991da9cf | ||
|
fc9e7249e7 | ||
|
9677406002 | ||
|
1e06f1da67 | ||
|
aa2eb7d40f | ||
|
6255f54f41 | ||
|
c2495545a5 | ||
|
fc277c75ee | ||
|
e544c01e63 | ||
|
c0f378e50a | ||
|
6b88c41ffc | ||
|
2e89ef444b | ||
|
fe950801c9 | ||
|
070a3343f2 | ||
|
48e47e86f1 | ||
|
4e0559833f | ||
|
8c18c000d4 | ||
|
a9fd6e2919 | ||
|
3cba7b52da | ||
|
32b747014d | ||
|
8e047596b1 | ||
|
172d6a1d08 | ||
|
abc8ce21f4 | ||
|
e574ca0331 | ||
|
1e5d2c0ed2 | ||
|
9ed5db044f | ||
|
0fed69f367 | ||
|
a1d6ad3453 | ||
|
c71f3110fb | ||
|
fe381503eb | ||
|
24ee761809 | ||
|
0b6e7515ce | ||
|
dd9fb09a29 | ||
|
6e42b4c896 | ||
|
56bd080b54 | ||
|
87f1bbdb7b | ||
|
bea356ac70 | ||
|
ce35a6f744 | ||
|
6363eb627d | ||
|
0f6946a88e | ||
|
22cdfc246c | ||
|
2d275a9891 | ||
|
93c13d2cea | ||
|
78e8888389 | ||
|
3b9abde89a | ||
|
4b3a41c535 | ||
|
b44000c9de | ||
|
727c1a45dd | ||
|
9daeb1cb34 | ||
|
3153a1b10b | ||
|
23055c9b2c | ||
|
8ef6c7ee7d | ||
|
eb1d3381f4 | ||
|
8cf279fbe1 | ||
|
ac8eddf3d7 | ||
|
bb50a7422d | ||
|
4afb57a1e5 | ||
|
3af14bc297 | ||
|
0bf8b5960d | ||
|
bae3601606 | ||
|
6234db924e | ||
|
d84a17688d | ||
|
f767fab4cc | ||
|
77a40745bb | ||
|
109c393cc6 | ||
|
77113a0cb2 | ||
|
1cd1ef690c | ||
|
c54e99d0a9 | ||
|
5ea614fe5d | ||
|
4e9f08e639 | ||
|
94ed283c51 | ||
|
67c06022e1 | ||
|
1bb9283edd | ||
|
9cea3c3abe | ||
|
68a19d55de | ||
|
3dd29ab07a | ||
|
c98d578757 | ||
|
311796a14b | ||
|
d3904e6c28 | ||
|
5a1fa14753 | ||
|
cff015f5d6 | ||
|
cc7572820e | ||
|
deb6840174 | ||
|
07c3211989 | ||
|
3a2da6afd3 | ||
|
afdb893dcd | ||
|
a92151d048 | ||
|
90ee05df93 | ||
|
9ce0f81d5f | ||
|
6f1db28ea2 | ||
|
a56145509d | ||
|
1b394ae417 | ||
|
c2a2fec067 | ||
|
49f1e5e6dc | ||
|
b8b787733c | ||
|
999ea3a443 | ||
|
46d6f455af | ||
|
4d6e9a5358 | ||
|
075d29acea | ||
|
84b19d7283 | ||
|
2f731000c1 | ||
|
c68e68897b | ||
|
b37a789f63 | ||
|
a6c8e1a11a | ||
|
929a4fc0c8 | ||
|
8836133f66 | ||
|
54dc9ddfaa | ||
|
c6843f9646 | ||
|
5849b13d3f | ||
|
5f343ec9e4 | ||
|
05b28e20bd | ||
|
ea60729c0a | ||
|
8cb6983550 | ||
|
f0f8363cf7 | ||
|
4a07432468 | ||
|
a51d72f42e | ||
|
6f5591c290 | ||
|
c13b574794 | ||
|
5c23accd9e | ||
|
9df740bcf1 | ||
|
14ece5ba9b | ||
|
cfdff383db | ||
|
4a7b50ed46 | ||
|
a0862ca547 | ||
|
8921752441 | ||
|
8e5b142c36 | ||
|
4b916c8e4d | ||
|
9f1552d98f | ||
|
a25c63a580 | ||
|
1701fcce5d | ||
|
889771fa7a | ||
|
21516170a9 | ||
|
da286d4439 | ||
|
51f99770fe | ||
|
aefd8bb36a | ||
|
40a7626acb | ||
|
6c967f177a | ||
|
1d684b7632 | ||
|
cbdad9cb1b | ||
|
c042dc8de3 | ||
|
f79d0febb1 | ||
|
10031547ec | ||
|
ccc90502ce | ||
|
6f0c40e841 | ||
|
2ba5f25205 | ||
|
b9eedb8689 | ||
|
a67b455306 | ||
|
27772946c2 | ||
|
4cdcbee0d6 | ||
|
97261a2feb | ||
|
0f7cf7e820 | ||
|
3802f6f8d2 | ||
|
1a345a7c65 | ||
|
ec58373a52 | ||
|
bf2629ffba | ||
|
baa7172a70 | ||
|
bc003fd78b | ||
|
41a5615ee5 | ||
|
df23405bde | ||
|
f94aa1aca3 | ||
|
96bda5515c | ||
|
0f6d830c19 | ||
|
e8ab82c2c1 | ||
|
a284c1f007 | ||
|
323408f8d9 | ||
|
90d3e314c0 | ||
|
4780ac334f | ||
|
b0bd91cd0f | ||
|
ae2754a568 | ||
|
6e07d18b1d | ||
|
f58db95be5 | ||
|
616b65c238 | ||
|
68cd051bb6 | ||
|
935d9aaba8 | ||
|
616f173aab | ||
|
d1cceb69b7 | ||
|
ff13ee1f27 | ||
|
dc84e7b9fb | ||
|
b82e8e860d | ||
|
98a8277a37 | ||
|
63ff44f8e5 | ||
|
dd5646ffbc | ||
|
371c8504c2 | ||
|
87b319d96a | ||
|
4c49259c92 | ||
|
83516aeb17 | ||
|
24602755cd | ||
|
11163146c0 | ||
|
3277343804 | ||
|
0740bb36f7 | ||
|
9c03dd3514 | ||
|
fbb3641406 | ||
|
f85b044ff8 | ||
|
ed4427243d | ||
|
9c5352ecd6 | ||
|
62fb5d1e14 | ||
|
14e693339c | ||
|
c58bce603b | ||
|
fa54b927f7 | ||
|
ba6bf3e217 | ||
|
6ecf8b820a | ||
|
a5dd905b46 | ||
|
676aaf3485 | ||
|
44e4bddc21 | ||
|
a03d1edac6 | ||
|
2938e33d0e | ||
|
2fb26525fa | ||
|
d0d1addba8 | ||
|
0fe3f181ad | ||
|
68de01e828 | ||
|
6e569c4f4a | ||
|
0fbda3c8c7 | ||
|
5bbb4abe21 | ||
|
1e50d4a93b | ||
|
ccf1791fca | ||
|
325120b653 | ||
|
2e1f36fe00 | ||
|
00d292f67e | ||
|
0e75dcc42a | ||
|
189e645b23 | ||
|
d5a8fb1e71 | ||
|
b2570f891d | ||
|
4035bf58f2 | ||
|
e5d4290938 | ||
|
8f58baf617 | ||
|
fded36c990 | ||
|
53f3e7d5e6 | ||
|
93e01b64d4 | ||
|
a871e147d6 | ||
|
378ca2c217 |
17
.babelrc
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false,
|
||||
"targets": {
|
||||
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
|
||||
}
|
||||
}],
|
||||
"stage-2"
|
||||
],
|
||||
"plugins": ["transform-vue-jsx", "transform-runtime"],
|
||||
"env": {
|
||||
"development":{
|
||||
"plugins": ["dynamic-import-node"]
|
||||
}
|
||||
}
|
||||
}
|
9
.env.development
Normal file
@@ -0,0 +1,9 @@
|
||||
VUE_APP_BASE_API = '/api'
|
||||
ENV = 'development'
|
||||
|
||||
// With this configuration, vue-cli uses babel-plugin-dynamic-import-node
|
||||
// It only does one thing by converting all import() to require()
|
||||
// So that all asynchronous components can be import synchronously using this plugin
|
||||
// This configuration can significantly increase the speed of hot updates when you have a large number of pages
|
||||
// https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js
|
||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
2
.env.production
Normal file
@@ -0,0 +1,2 @@
|
||||
VUE_APP_BASE_API = '/api'
|
||||
ENV = 'production'
|
3
.env.staging
Normal file
@@ -0,0 +1,3 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_BASE_API = '/api'
|
||||
ENV = 'staging'
|
33
.eslintrc.js
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
@@ -9,22 +9,22 @@ module.exports = {
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
extends: 'eslint:recommended',
|
||||
// required to lint *.vue files
|
||||
plugins: [
|
||||
'html'
|
||||
],
|
||||
// check if imports actually resolve
|
||||
'settings': {
|
||||
'import/resolver': {
|
||||
'webpack': {
|
||||
'config': 'build/webpack.base.conf.js'
|
||||
}
|
||||
}
|
||||
},
|
||||
extends: ['plugin:vue/recommended', 'eslint:recommended'],
|
||||
|
||||
// add your custom rules here
|
||||
//it is base on https://github.com/vuejs/eslint-config-vue
|
||||
'rules': {
|
||||
rules: {
|
||||
"vue/max-attributes-per-line": [2, {
|
||||
"singleline": 10,
|
||||
"multiline": {
|
||||
"max": 1,
|
||||
"allowFirstLine": false
|
||||
}
|
||||
}],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline":"off",
|
||||
"vue/name-property-casing": ["error", "PascalCase"],
|
||||
"vue/no-v-html": "off",
|
||||
'accessor-pairs': 2,
|
||||
'arrow-spacing': [2, {
|
||||
'before': true,
|
||||
@@ -47,7 +47,7 @@ module.exports = {
|
||||
'curly': [2, 'multi-line'],
|
||||
'dot-location': [2, 'property'],
|
||||
'eol-last': 2,
|
||||
'eqeqeq': [2, 'allow-null'],
|
||||
'eqeqeq': ["error", "always", {"null": "ignore"}],
|
||||
'generator-star-spacing': [2, {
|
||||
'before': true,
|
||||
'after': true
|
||||
@@ -196,4 +196,3 @@ module.exports = {
|
||||
'array-bracket-spacing': [2, 'never']
|
||||
}
|
||||
}
|
||||
|
||||
|
6
.gitignore
vendored
@@ -6,8 +6,8 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
**/*.log
|
||||
|
||||
test/unit/coverage
|
||||
test/e2e/reports
|
||||
tests/**/coverage/
|
||||
tests/e2e/reports
|
||||
selenium-debug.log
|
||||
|
||||
# Editor directories and files
|
||||
@@ -17,5 +17,7 @@ selenium-debug.log
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.local
|
||||
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
@@ -2,8 +2,6 @@
|
||||
|
||||
module.exports = {
|
||||
"plugins": {
|
||||
"postcss-import": {},
|
||||
"postcss-url": {},
|
||||
// to edit target browsers: use "browserslist" field in package.json
|
||||
"autoprefixer": {}
|
||||
}
|
||||
|
54
README.md
@@ -4,10 +4,10 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/vuejs/vue">
|
||||
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
|
||||
<img src="https://img.shields.io/badge/vue-2.5.17-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://github.com/ElemeFE/element">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.4.11-brightgreen.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
|
||||
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
|
||||
@@ -18,6 +18,12 @@
|
||||
<a href="https://github.com/PanJiaChen/vue-element-admin/releases">
|
||||
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
|
||||
</a>
|
||||
<a href="https://gitter.im/vue-element-admin/discuss">
|
||||
<img src="https://badges.gitter.im/Join%20Chat.svg" alt="gitter">
|
||||
</a>
|
||||
<a href="https://panjiachen.github.io/vue-element-admin-site/donate">
|
||||
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
English | [简体中文](./README.zh-CN.md)
|
||||
@@ -42,23 +48,34 @@ It is a magical vue admin based on the newest development stack of vue, built-in
|
||||
|
||||
**This project is positioned as a background integration solution and is not suitable for secondary development as a basic template.**
|
||||
|
||||
- Base template recommends using: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)
|
||||
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
|
||||
- Base template recommends using: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
|
||||
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
|
||||
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
|
||||
|
||||
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
|
||||
|
||||
**Note: This project uses element-ui@2.3.0+ version, so the minimum compatible vue@2.5.0+**
|
||||
|
||||
**Start using `webpack4` from `v3.8.0`. If you still want to continue using `webpack3`, please use this branch [webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
|
||||
|
||||
## Preparation
|
||||
|
||||
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
|
||||
Understanding and learning this knowledge in advance will greatly help the use of this project.
|
||||
Understanding and learning this knowledge in advance will greatly help the use of this project.
|
||||
|
||||
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
|
||||
|
||||
**Note: This project uses element-ui@2.3.0+ version, so the minimum compatible vue@2.5.0+**
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
|
||||
</p>
|
||||
|
||||
## Sponsors
|
||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||
|
||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||
|
||||
## Features
|
||||
|
||||
```
|
||||
- Login / Logout
|
||||
|
||||
@@ -75,7 +92,7 @@ You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) l
|
||||
- Multiple dynamic themes
|
||||
- Dynamic sidebar (supports multi-level routing)
|
||||
- Dynamic breadcrumb
|
||||
- Tags-view(Tab page Support right-click operation)
|
||||
- Tags-view (Tab page Support right-click operation)
|
||||
- Svg Sprite
|
||||
- Mock data
|
||||
- Screenfull
|
||||
@@ -106,6 +123,7 @@ You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) l
|
||||
- Avatar Upload
|
||||
- Back To Top
|
||||
- Drag Dialog
|
||||
- Drag Select
|
||||
- Drag Kanban
|
||||
- Drag List
|
||||
- SplitPane
|
||||
@@ -117,7 +135,7 @@ You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) l
|
||||
- Error Log
|
||||
- Dashboard
|
||||
- Guide Page
|
||||
- Echarts
|
||||
- ECharts
|
||||
- Clipboard
|
||||
- Markdown to html
|
||||
```
|
||||
@@ -138,6 +156,7 @@ npm run dev
|
||||
This will automatically open http://localhost:9527.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# build for test environment
|
||||
npm run build:sit
|
||||
@@ -147,10 +166,14 @@ npm run build:prod
|
||||
```
|
||||
|
||||
## Advanced
|
||||
|
||||
```bash
|
||||
# --report to build with bundle size analytics
|
||||
npm run build:prod --report
|
||||
|
||||
# --generate a bundle size analytics. default: bundle-report.html
|
||||
npm run build:prod --generate_report
|
||||
|
||||
# --preview to start a server in local to preview
|
||||
npm run build:prod --preview
|
||||
|
||||
@@ -164,12 +187,15 @@ npm run lint -- --fix
|
||||
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
|
||||
|
||||
## Changelog
|
||||
|
||||
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
|
||||
|
||||
## Online Demo
|
||||
|
||||
[Preview](http://panjiachen.github.io/vue-element-admin)
|
||||
|
||||
## Donate
|
||||
|
||||
If you find this project useful, you can buy author a glass of juice :tropical_drink:
|
||||
|
||||

|
||||
@@ -178,6 +204,14 @@ If you find this project useful, you can buy author a glass of juice :tropical_d
|
||||
|
||||
[Buy me a coffee](https://www.buymeacoffee.com/Pan)
|
||||
|
||||
## Browsers support
|
||||
|
||||
Modern browsers and Internet Explorer 10+.
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| --------- | --------- | --------- | --------- |
|
||||
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
|
||||
</a>
|
||||
<a href="https://github.com/ElemeFE/element">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui">
|
||||
<img src="https://img.shields.io/badge/element--ui-2.4.11-brightgreen.svg" alt="element-ui">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
|
||||
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
|
||||
@@ -18,59 +18,76 @@
|
||||
<a href="https://github.com/PanJiaChen/vue-element-admin/releases">
|
||||
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
|
||||
</a>
|
||||
<a href="https://gitter.im/vue-element-admin/discuss">
|
||||
<img src="https://badges.gitter.im/Join%20Chat.svg" alt="gitter">
|
||||
</a>
|
||||
<a href="https://panjiachen.gitee.io/vue-element-admin-site/zh/donate">
|
||||
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
简体中文 | [English](./README.md)
|
||||
|
||||
## 简介
|
||||
|
||||
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) 是一个后台集成解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈,内置了i18国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
|
||||
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) 是一个后台集成解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
|
||||
|
||||
- [在线访问](http://panjiachen.github.io/vue-element-admin)
|
||||
|
||||
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
|
||||
|
||||
- [Gitter讨论组](https://gitter.im/vue-element-admin/discuss)
|
||||
- [Gitter 讨论组](https://gitter.im/vue-element-admin/discuss)
|
||||
|
||||
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
|
||||
|
||||
- [Donate](https://panjiachen.github.io/vue-element-admin-site/zh/donate/)
|
||||
- [Donate](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate)
|
||||
|
||||
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
|
||||
|
||||
- [国内访问文档](https://panjiachen.gitee.io/vue-element-admin-site/zh/) 方便没翻墙的用户查看文档
|
||||
|
||||
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
|
||||
- 模板建议使用: [vueAdmin-template](https://github.com/PanJiaChen/vueAdmin-template)
|
||||
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
|
||||
|
||||
- 模板建议使用: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
|
||||
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
|
||||
- Typescript版: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
|
||||
|
||||
群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西,或者加入[qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602)
|
||||
|
||||
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
|
||||
|
||||
**从`v3.8.0`开始使用`webpack4`。所以若还想使用`webpack3`开发,请使用该分支[webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
|
||||
|
||||
**该项目不支持低版本浏览器(如 ie),有需求请自行添加 polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
|
||||
|
||||
## 前序准备
|
||||
|
||||
你需要在本地安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。本项目技术栈基于 [ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
|
||||
|
||||
同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
|
||||
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
|
||||
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
|
||||
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
|
||||
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
|
||||
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
|
||||
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
|
||||
- [手摸手,带你用合理的姿势使用webpack4(上)](https://juejin.im/post/5b56909a518825195f499806)
|
||||
- [手摸手,带你用合理的姿势使用webpack4(下)](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)
|
||||
|
||||
或者加入该群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西
|
||||
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
|
||||
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
|
||||
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
|
||||
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
|
||||
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
|
||||
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
|
||||
- [手摸手,带你用合理的姿势使用 webpack4(上)](https://juejin.im/post/5b56909a518825195f499806)
|
||||
- [手摸手,带你用合理的姿势使用 webpack4(下)](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)
|
||||
|
||||
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
|
||||
|
||||
**本项目并不是一个脚手架,更倾向于是一个集成解决方案**
|
||||
|
||||
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
|
||||
|
||||
**该项目不支持低版本浏览器(如ie),有需求请自行添加polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
|
||||
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
|
||||
|
||||
<p align="center">
|
||||
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
|
||||
</p>
|
||||
|
||||
## Sponsors
|
||||
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
|
||||
|
||||
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
|
||||
|
||||
## 功能
|
||||
|
||||
```
|
||||
- 登录 / 注销
|
||||
|
||||
@@ -118,6 +135,7 @@
|
||||
- 头像上传
|
||||
- 返回顶部
|
||||
- 拖拽Dialog
|
||||
- 拖拽Select
|
||||
- 拖拽看板
|
||||
- 列表拖拽
|
||||
- SplitPane
|
||||
@@ -129,28 +147,31 @@
|
||||
- 错误日志
|
||||
- Dashboard
|
||||
- 引导页
|
||||
- Echarts 图表
|
||||
- ECharts 图表
|
||||
- Clipboard(剪贴复制)
|
||||
- Markdown2html
|
||||
```
|
||||
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://github.com/PanJiaChen/vue-element-admin.git
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 建议不要用cnpm安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
|
||||
|
||||
# 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
|
||||
npm install --registry=https://registry.npm.taobao.org
|
||||
|
||||
# 启动服务
|
||||
npm run dev
|
||||
```
|
||||
|
||||
浏览器访问 http://localhost:9527
|
||||
|
||||
## 发布
|
||||
|
||||
```bash
|
||||
# 构建测试环境
|
||||
npm run build:sit
|
||||
@@ -160,9 +181,13 @@ npm run build:prod
|
||||
```
|
||||
|
||||
## 其它
|
||||
|
||||
```bash
|
||||
# --report to build with bundle size analytics
|
||||
npm run build:prod --report
|
||||
npm run build:prod
|
||||
|
||||
# --generate a bundle size analytics. default: bundle-report.html
|
||||
npm run build:prod --generate_report
|
||||
|
||||
# --preview to start a server in local to preview
|
||||
npm run build:prod --preview
|
||||
@@ -177,17 +202,30 @@ npm run lint -- --fix
|
||||
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
|
||||
|
||||
## Changelog
|
||||
|
||||
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
|
||||
|
||||
## Online Demo
|
||||
|
||||
[在线 Demo](http://panjiachen.github.io/vue-element-admin)
|
||||
|
||||
## Donate
|
||||
|
||||
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
|
||||

|
||||
|
||||
[更多捐赠方式](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate)
|
||||
|
||||
[Paypal Me](https://www.paypal.me/panfree23)
|
||||
|
||||
## Browsers support
|
||||
|
||||
Modern browsers and Internet Explorer 10+.
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| --------- | --------- | --------- | --------- |
|
||||
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
|
||||
|
5
babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
52
bin/index.js
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict'
|
||||
|
||||
const checker = require('../lib/checkSystem.js')
|
||||
const colors = require('colors')
|
||||
|
||||
// set color theme
|
||||
colors.setTheme({
|
||||
success: 'green',
|
||||
info: 'grey',
|
||||
warn: 'yellow',
|
||||
error: 'red',
|
||||
boldWarn: ['bold', 'yellow'],
|
||||
boldUnderlineSuccess: ['bold', 'underline', 'green'],
|
||||
boldUnderlineError: ['bold', 'underline', 'red']
|
||||
})
|
||||
|
||||
console.log('Checking versions...'.info, '\n')
|
||||
|
||||
checker(process.argv[2]).then((result) => {
|
||||
// check if the process should exit prematurely
|
||||
if (result.status != 0) {
|
||||
console.log(colors[result.message.type](result.message.text))
|
||||
process.exit(result.status)
|
||||
}
|
||||
|
||||
// print out results for each package
|
||||
result.packages.forEach((p) => {
|
||||
if (p.type === 'success') {
|
||||
console.log(('✔ ' + colors.bold(p.name) + ' was validated with ' + p.expectedVersion + '.').success)
|
||||
} else if (p.type === 'warn') {
|
||||
console.log((colors.bold(p.name) + ' was expected, but no validator found!').warn)
|
||||
} else if (p.type === 'error' && p.commandError) {
|
||||
console.log(('✘ Error validating ' + colors.bold(p.name) + ': ' + p.commandError).error)
|
||||
} else if (p.type === 'error' && !p.commandError) {
|
||||
console.log((
|
||||
'✘ ' + colors.bold(p.name) +
|
||||
' version is incorrect! Expected ' +
|
||||
p.expectedVersion + ' but was ' + p.foundVersion + '.'
|
||||
).error)
|
||||
}
|
||||
})
|
||||
|
||||
// print out a summary message
|
||||
if (result.message.type === 'success') {
|
||||
console.log('\n', result.message.text.boldUnderlineSuccess)
|
||||
} else {
|
||||
console.log('\n', result.message.text.boldUnderlineError)
|
||||
process.exit(1)
|
||||
}
|
||||
})
|
@@ -1,56 +0,0 @@
|
||||
'use strict'
|
||||
require('./check-versions')()
|
||||
|
||||
const ora = require('ora')
|
||||
const rm = require('rimraf')
|
||||
const path = require('path')
|
||||
const chalk = require('chalk')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const webpackConfig = require('./webpack.prod.conf')
|
||||
var connect = require('connect');
|
||||
var serveStatic = require('serve-static')
|
||||
|
||||
const spinner = ora('building for ' + process.env.env_config + ' environment...')
|
||||
spinner.start()
|
||||
|
||||
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
|
||||
if (err) throw err
|
||||
webpack(webpackConfig, (err, stats) => {
|
||||
spinner.stop()
|
||||
if (err) throw err
|
||||
process.stdout.write(stats.toString({
|
||||
colors: true,
|
||||
modules: false,
|
||||
children: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
}) + '\n\n')
|
||||
|
||||
if (stats.hasErrors()) {
|
||||
console.log(chalk.red(' Build failed with errors.\n'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(chalk.cyan(' Build complete.\n'))
|
||||
console.log(chalk.yellow(
|
||||
' Tip: built files are meant to be served over an HTTP server.\n' +
|
||||
' Opening index.html over file:// won\'t work.\n'
|
||||
))
|
||||
|
||||
if (process.env.npm_config_preview) {
|
||||
const port = 9526
|
||||
const host = "http://localhost:" + port
|
||||
const basePath = config.build.assetsPublicPath
|
||||
const app = connect()
|
||||
|
||||
app.use(basePath, serveStatic('./dist', {
|
||||
'index': ['index.html', '/']
|
||||
}))
|
||||
|
||||
app.listen(port, function () {
|
||||
console.log(chalk.green(`> Listening at http://localhost:${port}${basePath}`))
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
@@ -1,54 +0,0 @@
|
||||
'use strict'
|
||||
const chalk = require('chalk')
|
||||
const semver = require('semver')
|
||||
const packageConfig = require('../package.json')
|
||||
const shell = require('shelljs')
|
||||
|
||||
function exec (cmd) {
|
||||
return require('child_process').execSync(cmd).toString().trim()
|
||||
}
|
||||
|
||||
const versionRequirements = [
|
||||
{
|
||||
name: 'node',
|
||||
currentVersion: semver.clean(process.version),
|
||||
versionRequirement: packageConfig.engines.node
|
||||
}
|
||||
]
|
||||
|
||||
if (shell.which('npm')) {
|
||||
versionRequirements.push({
|
||||
name: 'npm',
|
||||
currentVersion: exec('npm --version'),
|
||||
versionRequirement: packageConfig.engines.npm
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = function () {
|
||||
const warnings = []
|
||||
|
||||
for (let i = 0; i < versionRequirements.length; i++) {
|
||||
const mod = versionRequirements[i]
|
||||
|
||||
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
|
||||
warnings.push(mod.name + ': ' +
|
||||
chalk.red(mod.currentVersion) + ' should be ' +
|
||||
chalk.green(mod.versionRequirement)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings.length) {
|
||||
console.log('')
|
||||
console.log(chalk.yellow('To use this template, you must update following to modules:'))
|
||||
console.log()
|
||||
|
||||
for (let i = 0; i < warnings.length; i++) {
|
||||
const warning = warnings[i]
|
||||
console.log(' ' + warning)
|
||||
}
|
||||
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
31
build/index.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const { run } = require('runjs')
|
||||
const chalk = require('chalk')
|
||||
const config = require('../vue.config.js')
|
||||
const rawArgv = process.argv.slice(2)
|
||||
const args = rawArgv.join(' ')
|
||||
|
||||
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
|
||||
run(`vue-cli-service build ${args}`)
|
||||
|
||||
const port = 9526
|
||||
const publicPath = config.publicPath
|
||||
|
||||
var connect = require('connect')
|
||||
var serveStatic = require('serve-static')
|
||||
const app = connect()
|
||||
|
||||
app.use(
|
||||
publicPath,
|
||||
serveStatic('./dist', {
|
||||
index: ['index.html', '/']
|
||||
})
|
||||
)
|
||||
|
||||
app.listen(port, function() {
|
||||
console.log(
|
||||
chalk.green(`> Listening at http://localhost:${port}${publicPath}`)
|
||||
)
|
||||
})
|
||||
} else {
|
||||
run(`vue-cli-service build ${args}`)
|
||||
}
|
BIN
build/logo.png
Before Width: | Height: | Size: 6.7 KiB |
101
build/utils.js
@@ -1,101 +0,0 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const config = require('../config')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const packageConfig = require('../package.json')
|
||||
|
||||
exports.assetsPath = function (_path) {
|
||||
const assetsSubDirectory = process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsSubDirectory
|
||||
: config.dev.assetsSubDirectory
|
||||
|
||||
return path.posix.join(assetsSubDirectory, _path)
|
||||
}
|
||||
|
||||
exports.cssLoaders = function (options) {
|
||||
options = options || {}
|
||||
|
||||
const cssLoader = {
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
sourceMap: options.sourceMap
|
||||
}
|
||||
}
|
||||
|
||||
const postcssLoader = {
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
sourceMap: options.sourceMap
|
||||
}
|
||||
}
|
||||
|
||||
// generate loader string to be used with extract text plugin
|
||||
function generateLoaders (loader, loaderOptions) {
|
||||
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
|
||||
|
||||
if (loader) {
|
||||
loaders.push({
|
||||
loader: loader + '-loader',
|
||||
options: Object.assign({}, loaderOptions, {
|
||||
sourceMap: options.sourceMap
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Extract CSS when that option is specified
|
||||
// (which is the case during production build)
|
||||
if (options.extract) {
|
||||
return ExtractTextPlugin.extract({
|
||||
use: loaders,
|
||||
fallback: 'vue-style-loader'
|
||||
})
|
||||
} else {
|
||||
return ['vue-style-loader'].concat(loaders)
|
||||
}
|
||||
}
|
||||
|
||||
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
|
||||
return {
|
||||
css: generateLoaders(),
|
||||
postcss: generateLoaders(),
|
||||
less: generateLoaders('less'),
|
||||
sass: generateLoaders('sass', { indentedSyntax: true }),
|
||||
scss: generateLoaders('sass'),
|
||||
stylus: generateLoaders('stylus'),
|
||||
styl: generateLoaders('stylus')
|
||||
}
|
||||
}
|
||||
|
||||
// Generate loaders for standalone style files (outside of .vue)
|
||||
exports.styleLoaders = function (options) {
|
||||
const output = []
|
||||
const loaders = exports.cssLoaders(options)
|
||||
|
||||
for (const extension in loaders) {
|
||||
const loader = loaders[extension]
|
||||
output.push({
|
||||
test: new RegExp('\\.' + extension + '$'),
|
||||
use: loader
|
||||
})
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
exports.createNotifierCallback = () => {
|
||||
const notifier = require('node-notifier')
|
||||
|
||||
return (severity, errors) => {
|
||||
if (severity !== 'error') return
|
||||
|
||||
const error = errors[0]
|
||||
const filename = error.file && error.file.split('!').pop()
|
||||
|
||||
notifier.notify({
|
||||
title: packageConfig.name,
|
||||
message: severity + ': ' + error.name,
|
||||
subtitle: filename || '',
|
||||
icon: path.join(__dirname, 'logo.png')
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
'use strict'
|
||||
const utils = require('./utils')
|
||||
const config = require('../config')
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
const sourceMapEnabled = isProduction
|
||||
? config.build.productionSourceMap
|
||||
: config.dev.cssSourceMap
|
||||
|
||||
module.exports = {
|
||||
loaders: utils.cssLoaders({
|
||||
sourceMap: sourceMapEnabled,
|
||||
extract: isProduction
|
||||
}),
|
||||
cssSourceMap: sourceMapEnabled,
|
||||
cacheBusting: config.dev.cacheBusting,
|
||||
transformToRequire: {
|
||||
video: ['src', 'poster'],
|
||||
source: 'src',
|
||||
img: 'src',
|
||||
image: 'xlink:href'
|
||||
}
|
||||
}
|
@@ -1,100 +0,0 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const utils = require('./utils')
|
||||
const config = require('../config')
|
||||
const vueLoaderConfig = require('./vue-loader.conf')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
const createLintingRule = () => ({
|
||||
test: /\.(js|vue)$/,
|
||||
loader: 'eslint-loader',
|
||||
enforce: 'pre',
|
||||
include: [resolve('src'), resolve('test')],
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter'),
|
||||
emitWarning: !config.dev.showEslintErrorsInOverlay
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
context: path.resolve(__dirname, '../'),
|
||||
entry: {
|
||||
app: './src/main.js'
|
||||
},
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: '[name].js',
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? config.build.assetsPublicPath
|
||||
: config.dev.assetsPublicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'@': resolve('src'),
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
...(config.dev.useEslint ? [createLintingRule()] : []),
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader',
|
||||
options: vueLoaderConfig
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader?cacheDirectory',
|
||||
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
loader: 'svg-sprite-loader',
|
||||
include: [resolve('src/icons')],
|
||||
options: {
|
||||
symbolId: 'icon-[name]'
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
exclude: [resolve('src/icons')],
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('img/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('media/[name].[hash:7].[ext]')
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000,
|
||||
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
node: {
|
||||
// prevent webpack from injecting useless setImmediate polyfill because Vue
|
||||
// source contains it (although only uses it if it's native).
|
||||
setImmediate: false,
|
||||
// prevent webpack from injecting mocks to Node native modules
|
||||
// that does not make sense for the client
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty'
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const utils = require('./utils')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const merge = require('webpack-merge')
|
||||
const baseWebpackConfig = require('./webpack.base.conf')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
|
||||
const portfinder = require('portfinder')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
const HOST = process.env.HOST
|
||||
const PORT = process.env.PORT && Number(process.env.PORT)
|
||||
|
||||
const devWebpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
|
||||
},
|
||||
// cheap-module-eval-source-map is faster for development
|
||||
devtool: config.dev.devtool,
|
||||
|
||||
// these devServer options should be customized in /config/index.js
|
||||
devServer: {
|
||||
clientLogLevel: 'warning',
|
||||
historyApiFallback: true,
|
||||
hot: true,
|
||||
compress: true,
|
||||
host: HOST || config.dev.host,
|
||||
port: PORT || config.dev.port,
|
||||
open: config.dev.autoOpenBrowser,
|
||||
overlay: config.dev.errorOverlay
|
||||
? { warnings: false, errors: true }
|
||||
: false,
|
||||
publicPath: config.dev.assetsPublicPath,
|
||||
proxy: config.dev.proxyTable,
|
||||
quiet: true, // necessary for FriendlyErrorsPlugin
|
||||
watchOptions: {
|
||||
poll: config.dev.poll,
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': require('../config/dev.env')
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
// https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: 'index.html',
|
||||
inject: true,
|
||||
favicon: resolve('favicon.ico'),
|
||||
title: 'vue-element-admin',
|
||||
path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
|
||||
}),
|
||||
]
|
||||
})
|
||||
|
||||
module.exports = new Promise((resolve, reject) => {
|
||||
portfinder.basePort = process.env.PORT || config.dev.port
|
||||
portfinder.getPort((err, port) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
// publish the new Port, necessary for e2e tests
|
||||
process.env.PORT = port
|
||||
// add port to devServer config
|
||||
devWebpackConfig.devServer.port = port
|
||||
|
||||
// Add FriendlyErrorsPlugin
|
||||
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
|
||||
compilationSuccessInfo: {
|
||||
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
|
||||
},
|
||||
onErrors: config.dev.notifyOnErrors
|
||||
? utils.createNotifierCallback()
|
||||
: undefined
|
||||
}))
|
||||
|
||||
resolve(devWebpackConfig)
|
||||
}
|
||||
})
|
||||
})
|
@@ -1,175 +0,0 @@
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
const utils = require('./utils')
|
||||
const webpack = require('webpack')
|
||||
const config = require('../config')
|
||||
const merge = require('webpack-merge')
|
||||
const baseWebpackConfig = require('./webpack.base.conf')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
|
||||
|
||||
function resolve (dir) {
|
||||
return path.join(__dirname, '..', dir)
|
||||
}
|
||||
|
||||
const env = require('../config/'+process.env.env_config+'.env')
|
||||
|
||||
const webpackConfig = merge(baseWebpackConfig, {
|
||||
module: {
|
||||
rules: utils.styleLoaders({
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
extract: true,
|
||||
usePostCSS: true
|
||||
})
|
||||
},
|
||||
devtool: config.build.productionSourceMap ? config.build.devtool : false,
|
||||
output: {
|
||||
path: config.build.assetsRoot,
|
||||
filename: utils.assetsPath('js/[name].[chunkhash].js'),
|
||||
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
|
||||
},
|
||||
plugins: [
|
||||
// http://vuejs.github.io/vue-loader/en/workflow/production.html
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': env
|
||||
}),
|
||||
new UglifyJsPlugin({
|
||||
uglifyOptions: {
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
},
|
||||
sourceMap: config.build.productionSourceMap,
|
||||
parallel: true
|
||||
}),
|
||||
// extract css into its own file
|
||||
new ExtractTextPlugin({
|
||||
filename: utils.assetsPath('css/[name].[contenthash].css'),
|
||||
// Setting the following option to `false` will not extract CSS from codesplit chunks.
|
||||
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
|
||||
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
|
||||
allChunks: false,
|
||||
}),
|
||||
// Compress extracted CSS. We are using this plugin so that possible
|
||||
// duplicated CSS from different components can be deduped.
|
||||
new OptimizeCSSPlugin({
|
||||
cssProcessorOptions: config.build.productionSourceMap
|
||||
? { safe: true, map: { inline: false } }
|
||||
: { safe: true }
|
||||
}),
|
||||
// generate dist index.html with correct asset hash for caching.
|
||||
// you can customize output by editing /index.html
|
||||
// see https://github.com/ampedandwired/html-webpack-plugin
|
||||
new HtmlWebpackPlugin({
|
||||
filename: config.build.index,
|
||||
template: 'index.html',
|
||||
inject: true,
|
||||
favicon: resolve('favicon.ico'),
|
||||
title: 'vue-element-admin',
|
||||
path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true
|
||||
// more options:
|
||||
// https://github.com/kangax/html-minifier#options-quick-reference
|
||||
},
|
||||
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
|
||||
chunksSortMode: 'dependency'
|
||||
}),
|
||||
// keep module.id stable when vender modules does not change
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
// enable scope hoisting
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
// split vendor js into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks (module) {
|
||||
// any required modules inside node_modules are extracted to vendor
|
||||
return (
|
||||
module.resource &&
|
||||
/\.js$/.test(module.resource) &&
|
||||
module.resource.indexOf(
|
||||
path.join(__dirname, '../node_modules')
|
||||
) === 0
|
||||
)
|
||||
}
|
||||
}),
|
||||
// extract webpack runtime and module manifest to its own file in order to
|
||||
// prevent vendor hash from being updated whenever app bundle is updated
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'manifest',
|
||||
minChunks: Infinity
|
||||
}),
|
||||
// This instance extracts shared chunks from code splitted chunks and bundles them
|
||||
// in a separate chunk, similar to the vendor chunk
|
||||
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'app',
|
||||
async: 'vendor-async',
|
||||
children: true,
|
||||
minChunks: 3
|
||||
}),
|
||||
// split echarts into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
async: 'echarts',
|
||||
minChunks(module) {
|
||||
var context = module.context;
|
||||
return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0);
|
||||
}
|
||||
}),
|
||||
// split xlsx into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
async: 'xlsx',
|
||||
minChunks(module) {
|
||||
var context = module.context;
|
||||
return context && (context.indexOf('xlsx') >= 0);
|
||||
}
|
||||
}),
|
||||
// split codemirror into its own file
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
async: 'codemirror',
|
||||
minChunks(module) {
|
||||
var context = module.context;
|
||||
return context && (context.indexOf('codemirror') >= 0);
|
||||
}
|
||||
}),
|
||||
|
||||
// copy custom static assets
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: path.resolve(__dirname, '../static'),
|
||||
to: config.build.assetsSubDirectory,
|
||||
ignore: ['.*']
|
||||
}
|
||||
])
|
||||
]
|
||||
})
|
||||
|
||||
if (config.build.productionGzip) {
|
||||
const CompressionWebpackPlugin = require('compression-webpack-plugin')
|
||||
|
||||
webpackConfig.plugins.push(
|
||||
new CompressionWebpackPlugin({
|
||||
asset: '[path].gz[query]',
|
||||
algorithm: 'gzip',
|
||||
test: new RegExp(
|
||||
'\\.(' +
|
||||
config.build.productionGzipExtensions.join('|') +
|
||||
')$'
|
||||
),
|
||||
threshold: 10240,
|
||||
minRatio: 0.8
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (config.build.bundleAnalyzerReport) {
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
|
||||
}
|
||||
|
||||
module.exports = webpackConfig
|
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
NODE_ENV: '"development"',
|
||||
ENV_CONFIG: '"dev"',
|
||||
BASE_API: '"https://api-dev"'
|
||||
}
|
@@ -1,91 +0,0 @@
|
||||
'use strict'
|
||||
// Template version: 1.2.6
|
||||
// see http://vuejs-templates.github.io/webpack for documentation.
|
||||
|
||||
const path = require('path')
|
||||
|
||||
module.exports = {
|
||||
dev: {
|
||||
|
||||
// Paths
|
||||
assetsSubDirectory: 'static',
|
||||
assetsPublicPath: '/',
|
||||
proxyTable: {},
|
||||
|
||||
// Various Dev Server settings
|
||||
|
||||
// can be overwritten by process.env.HOST
|
||||
// if you want dev by ip, please set host: '0.0.0.0'
|
||||
host: 'localhost',
|
||||
port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
|
||||
autoOpenBrowser: true,
|
||||
errorOverlay: true,
|
||||
notifyOnErrors: false,
|
||||
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
|
||||
|
||||
// Use Eslint Loader?
|
||||
// If true, your code will be linted during bundling and
|
||||
// linting errors and warnings will be shown in the console.
|
||||
useEslint: true,
|
||||
// If true, eslint errors and warnings will also be shown in the error overlay
|
||||
// in the browser.
|
||||
showEslintErrorsInOverlay: false,
|
||||
|
||||
/**
|
||||
* Source Maps
|
||||
*/
|
||||
|
||||
// https://webpack.js.org/configuration/devtool/#development
|
||||
devtool: '#cheap-source-map',
|
||||
|
||||
// If you have problems debugging vue-files in devtools,
|
||||
// set this to false - it *may* help
|
||||
// https://vue-loader.vuejs.org/en/options.html#cachebusting
|
||||
cacheBusting: true,
|
||||
|
||||
// CSS Sourcemaps off by default because relative paths are "buggy"
|
||||
// with this option, according to the CSS-Loader README
|
||||
// (https://github.com/webpack/css-loader#sourcemaps)
|
||||
// In our experience, they generally work as expected,
|
||||
// just be aware of this issue when enabling this option.
|
||||
cssSourceMap: false,
|
||||
},
|
||||
|
||||
build: {
|
||||
// Template for index.html
|
||||
index: path.resolve(__dirname, '../dist/index.html'),
|
||||
|
||||
// Paths
|
||||
assetsRoot: path.resolve(__dirname, '../dist'),
|
||||
assetsSubDirectory: 'static',
|
||||
|
||||
/**
|
||||
* You can set by youself according to actual condition
|
||||
* You will need to set this if you plan to deploy your site under a sub path,
|
||||
* for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
|
||||
* then assetsPublicPath should be set to "/bar/".
|
||||
* In most cases please use '/' !!!
|
||||
*/
|
||||
assetsPublicPath: '/vue-element-admin/', // If you are deployed on the root path, please use '/'
|
||||
|
||||
/**
|
||||
* Source Maps
|
||||
*/
|
||||
productionSourceMap: false,
|
||||
// https://webpack.js.org/configuration/devtool/#production
|
||||
devtool: '#source-map',
|
||||
|
||||
// Gzip off by default as many popular static hosts such as
|
||||
// Surge or Netlify already gzip all static assets for you.
|
||||
// Before setting to `true`, make sure to:
|
||||
// npm install --save-dev compression-webpack-plugin
|
||||
productionGzip: false,
|
||||
productionGzipExtensions: ['js', 'css'],
|
||||
|
||||
// Run the build command with an extra argument to
|
||||
// View the bundle analyzer report after build finishes:
|
||||
// `npm run build --report`
|
||||
// Set to `true` or `false` to always turn it on or off
|
||||
bundleAnalyzerReport: process.env.npm_config_report
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"',
|
||||
ENV_CONFIG: '"prod"',
|
||||
BASE_API: '"https://api-prod"'
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
NODE_ENV: '"production"',
|
||||
ENV_CONFIG: '"sit"',
|
||||
BASE_API: '"https://api-sit"'
|
||||
}
|
27
jest.config.js
Normal file
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
verbose: true,
|
||||
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
|
||||
// transformIgnorePatterns: [
|
||||
// 'node_modules/(?!(babel-jest|jest-vue-preprocessor)/)'
|
||||
// ],
|
||||
transform: {
|
||||
'^.+\\.vue$': 'vue-jest',
|
||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||
'^.+\\.jsx?$': 'babel-jest'
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1'
|
||||
},
|
||||
snapshotSerializers: ['jest-serializer-vue'],
|
||||
testMatch: [
|
||||
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
|
||||
],
|
||||
collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
|
||||
coverageDirectory: '<rootDir>/tests/unit/coverage',
|
||||
// 'collectCoverage': true,
|
||||
'coverageReporters': [
|
||||
'lcov',
|
||||
'text-summary'
|
||||
],
|
||||
testURL: 'http://localhost/'
|
||||
}
|
122
lib/checkSystem.js
Normal file
@@ -0,0 +1,122 @@
|
||||
'use strict'
|
||||
|
||||
const check = function(pathToPackage) {
|
||||
const path = require('path')
|
||||
const Promise = require('bluebird')
|
||||
const exec = Promise.promisify(require('child_process').exec)
|
||||
const _ = require('lodash')
|
||||
const fs = require('fs')
|
||||
const jsonfile = require('jsonfile')
|
||||
const validaterRules = require('./validatorRules')
|
||||
const promiseHelpers = require('./promiseHelpers')
|
||||
|
||||
let engines
|
||||
|
||||
const checkerResult = {
|
||||
status: 0,
|
||||
message: '',
|
||||
packages: []
|
||||
}
|
||||
|
||||
const packageJsonPath = pathToPackage || path.join(process.cwd(), 'package.json')
|
||||
try {
|
||||
fs.accessSync(packageJsonPath)
|
||||
engines = jsonfile.readFileSync(packageJsonPath).engines
|
||||
} catch (ex) {
|
||||
checkerResult.message = {
|
||||
text: '✘ No package.json found in the current directory so I can\'t validate what you need!',
|
||||
type: 'error'
|
||||
}
|
||||
checkerResult.status = -1
|
||||
|
||||
return Promise.resolve(checkerResult)
|
||||
}
|
||||
|
||||
if (!engines) {
|
||||
checkerResult.message = {
|
||||
text: '✘ No engines found in package.json so I can\'t validate what you need!',
|
||||
type: 'error'
|
||||
}
|
||||
checkerResult.status = -1
|
||||
|
||||
return Promise.resolve(checkerResult)
|
||||
}
|
||||
|
||||
const thingsToCheck = Object.getOwnPropertyNames(engines)
|
||||
const validatorPromises = thingsToCheck.map(validate) // run the function over all items.
|
||||
|
||||
return promiseHelpers.allSettled(validatorPromises)
|
||||
.then((inspections) => {
|
||||
const environmentIsValid = _.every(inspections,
|
||||
(inspection) => inspection.isFulfilled() && inspection.value())
|
||||
|
||||
if (environmentIsValid) {
|
||||
checkerResult.message = {
|
||||
text: 'Environment looks good!',
|
||||
type: 'success'
|
||||
}
|
||||
} else {
|
||||
checkerResult.message = {
|
||||
text: 'Environment is invalid!',
|
||||
type: 'error'
|
||||
}
|
||||
}
|
||||
return checkerResult
|
||||
})
|
||||
|
||||
function validate(name) {
|
||||
// find it in the validators
|
||||
const validator = validaterRules[name]
|
||||
|
||||
if (validator === undefined) {
|
||||
checkerResult.packages.push({
|
||||
name: name,
|
||||
validatorFound: false,
|
||||
type: 'warn'
|
||||
})
|
||||
return Promise.resolve(false)
|
||||
}
|
||||
|
||||
// call the validator and pass in the version we expect
|
||||
return execAndCheck(validator, engines[name]).then((results) => {
|
||||
if (results.result) {
|
||||
checkerResult.packages.push({
|
||||
name: name,
|
||||
validatorFound: true,
|
||||
expectedVersion: engines[name],
|
||||
foundVersion: engines[name],
|
||||
type: 'success'
|
||||
})
|
||||
} else {
|
||||
checkerResult.packages.push({
|
||||
name: name,
|
||||
validatorFound: true,
|
||||
expectedVersion: engines[name],
|
||||
foundVersion: results.reason.trim() || 'missing',
|
||||
type: 'error'
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.resolve(results.result)
|
||||
}).catch((error) => {
|
||||
checkerResult.packages.push({
|
||||
name: name,
|
||||
validatorFound: true,
|
||||
expectedVersion: engines[name],
|
||||
commandError: error,
|
||||
type: 'error'
|
||||
})
|
||||
return Promise.reject()
|
||||
})
|
||||
}
|
||||
|
||||
function execAndCheck(validator, expectedVersion) {
|
||||
return exec(validator.versionCheck).then((result) => {
|
||||
return {
|
||||
result: validator.versionValidate(result, expectedVersion),
|
||||
reason: result
|
||||
}
|
||||
}).catch((e) => { throw e })
|
||||
}
|
||||
}
|
||||
module.exports = check
|
24
lib/promiseHelpers.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const Promise = require('bluebird')
|
||||
|
||||
/**
|
||||
* Creates a promise that is resolved when all input promises have been
|
||||
* settled. The returned Promise is resolved with an array of
|
||||
* Promise.Inspection objects.
|
||||
*
|
||||
* This is the commonly accepted way of implementing allSettled() in Bluebird.
|
||||
* See: http://bluebirdjs.com/docs/api/reflect.html
|
||||
*
|
||||
* @param promises - The array of input promises.
|
||||
* @returns {Promise<Promise.Inspection[]>} A promise that will be resolved once
|
||||
* all input Promises have settled. The returned Promise will be resolved with a
|
||||
* corresponding array of Promise.Inspection objects.
|
||||
*/
|
||||
function allSettled(promises) {
|
||||
'use strict'
|
||||
const wrappedPromises = promises.map((curPromise) => curPromise.reflect())
|
||||
return Promise.all(wrappedPromises)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
allSettled: allSettled
|
||||
}
|
141
lib/validatorRules.js
Normal file
@@ -0,0 +1,141 @@
|
||||
'use strict'
|
||||
|
||||
const semver = require('semver')
|
||||
|
||||
module.exports = {
|
||||
osx: {
|
||||
versionCheck: 'sw_vers -productVersion',
|
||||
versionValidate:
|
||||
(detectedVersion, expectedVersion) => expectedVersion === detectedVersion.trim()
|
||||
},
|
||||
node: {
|
||||
versionCheck: 'node -v',
|
||||
versionValidate:
|
||||
(detectedVersion, expectedVersion) => semver.satisfies(detectedVersion, expectedVersion)
|
||||
},
|
||||
npm: {
|
||||
versionCheck: 'npm -v',
|
||||
versionValidate:
|
||||
(detectedVersion, expectedVersion) => semver.satisfies(detectedVersion, expectedVersion)
|
||||
},
|
||||
jx: {
|
||||
versionCheck: 'jx -jxv',
|
||||
versionValidate:
|
||||
(result, version) => 'v' + version === result.trim()
|
||||
},
|
||||
cordova: {
|
||||
versionCheck: 'cordova -v',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
appium: {
|
||||
versionCheck: 'appium -v',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
'ios-deploy': {
|
||||
versionCheck: 'ios-deploy -V',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
'ios-sim': {
|
||||
versionCheck: 'ios-sim --version',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
bower: {
|
||||
versionCheck: 'bower -v',
|
||||
versionValidate:
|
||||
(result, version) => semver.satisfies(result, version)
|
||||
},
|
||||
'ios-webkit-debug-proxy': {
|
||||
versionCheck: 'brew list ios-webkit-debug-proxy --versions',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
|
||||
},
|
||||
'ideviceinstaller': {
|
||||
versionCheck: 'brew list ideviceinstaller --versions',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
java: {
|
||||
versionCheck: 'javac -version 2>&1',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
ant: {
|
||||
versionCheck: 'ant -version',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
adb: {
|
||||
versionCheck: 'adb version',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
git: {
|
||||
versionCheck: 'git --version',
|
||||
versionValidate:
|
||||
(result, version) => {
|
||||
// http://stackoverflow.com/questions/82064/a-regex-for-version-number-parsing
|
||||
const found = result.match(/(\d+\.)?(\d+\.)?(\d+)/i)
|
||||
return found[0] === version
|
||||
}
|
||||
},
|
||||
windows: {
|
||||
versionCheck: 'ver',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
'gulp-cli': {
|
||||
versionCheck: 'npm list --depth=0 -g |grep gulp-cli',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
cocoapods: {
|
||||
versionCheck: 'pod --version',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
xcodebuild: {
|
||||
versionCheck: 'xcodebuild -version',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
carthage: {
|
||||
versionCheck: 'carthage version',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
xcpretty: {
|
||||
versionCheck: 'xcpretty -v',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
},
|
||||
libimobiledevice: {
|
||||
versionCheck: 'brew list --versions |grep libimobiledevice',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
'deviceconsole': {
|
||||
versionCheck: 'npm list --depth=0 -g |grep deviceconsole',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
'check-engine': {
|
||||
versionCheck: 'npm list --depth=0 -g |grep check-engine',
|
||||
versionValidate:
|
||||
(result, version) => result.includes(version)
|
||||
},
|
||||
yarn: {
|
||||
versionCheck: 'yarn -v',
|
||||
versionValidate:
|
||||
(result, version) => semver.satisfies(result, version)
|
||||
},
|
||||
nsp: {
|
||||
versionCheck: 'nsp --version',
|
||||
versionValidate:
|
||||
(result, version) => version === result.trim()
|
||||
}
|
||||
}
|
116
mock/article.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const List = []
|
||||
const count = 100
|
||||
|
||||
const baseContent = '<p>我是测试数据我是测试数据</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
|
||||
const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3'
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
List.push(Mock.mock({
|
||||
id: '@increment',
|
||||
timestamp: +Mock.Random.date('T'),
|
||||
author: '@first',
|
||||
reviewer: '@first',
|
||||
title: '@title(5, 10)',
|
||||
content_short: 'mock data',
|
||||
content: baseContent,
|
||||
forecast: '@float(0, 100, 2, 2)',
|
||||
importance: '@integer(1, 3)',
|
||||
'type|1': ['CN', 'US', 'JP', 'EU'],
|
||||
'status|1': ['published', 'draft', 'deleted'],
|
||||
display_time: '@datetime',
|
||||
comment_disabled: true,
|
||||
pageviews: '@integer(300, 5000)',
|
||||
image_uri,
|
||||
platforms: ['a-platform']
|
||||
}))
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/article/list',
|
||||
type: 'get',
|
||||
response: config => {
|
||||
const { importance, type, title, page = 1, limit = 20, sort } = config.query
|
||||
|
||||
let mockList = List.filter(item => {
|
||||
if (importance && item.importance !== +importance) return false
|
||||
if (type && item.type !== type) return false
|
||||
if (title && item.title.indexOf(title) < 0) return false
|
||||
return true
|
||||
})
|
||||
|
||||
if (sort === '-id') {
|
||||
mockList = mockList.reverse()
|
||||
}
|
||||
|
||||
const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
|
||||
|
||||
return {
|
||||
code: 20000,
|
||||
data: {
|
||||
total: mockList.length,
|
||||
items: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
url: '/article/detail',
|
||||
type: 'get',
|
||||
response: config => {
|
||||
const { id } = config.query
|
||||
for (const article of List) {
|
||||
if (article.id === +id) {
|
||||
return {
|
||||
code: 20000,
|
||||
data: article
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
url: '/article/pv',
|
||||
type: 'get',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: {
|
||||
pvData: [
|
||||
{ key: 'PC', pv: 1024 },
|
||||
{ key: 'mobile', pv: 1024 },
|
||||
{ key: 'ios', pv: 1024 },
|
||||
{ key: 'android', pv: 1024 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
url: '/article/create',
|
||||
type: 'post',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
url: '/article/update',
|
||||
type: 'post',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
53
mock/index.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import Mock from 'mockjs'
|
||||
import mocks from './mocks'
|
||||
import { param2Obj } from '../src/utils'
|
||||
|
||||
const MOCK_API_BASE = '/mock'
|
||||
|
||||
export function mockXHR() {
|
||||
// 修复在使用 MockJS 情况下,设置 withCredentials = true,且未被拦截的跨域请求丢失 Cookies 的问题
|
||||
// https://github.com/nuysoft/Mock/issues/300
|
||||
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
|
||||
Mock.XHR.prototype.send = function() {
|
||||
if (this.custom.xhr) {
|
||||
this.custom.xhr.withCredentials = this.withCredentials || false
|
||||
}
|
||||
this.proxy_send(...arguments)
|
||||
}
|
||||
|
||||
function XHR2ExpressReqWrap(respond) {
|
||||
return function(options) {
|
||||
let result = null
|
||||
if (respond instanceof Function) {
|
||||
const { body, type, url } = options
|
||||
// https://expressjs.com/en/4x/api.html#req
|
||||
result = respond({
|
||||
method: type,
|
||||
body: JSON.parse(body),
|
||||
query: param2Obj(url)
|
||||
})
|
||||
} else {
|
||||
result = respond
|
||||
}
|
||||
return Mock.mock(result)
|
||||
}
|
||||
}
|
||||
|
||||
for (const i of mocks) {
|
||||
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
|
||||
}
|
||||
}
|
||||
|
||||
const responseFake = (url, type, respond) => {
|
||||
return {
|
||||
url: new RegExp(`${MOCK_API_BASE}${url}`),
|
||||
type: type || 'get',
|
||||
response(req, res) {
|
||||
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default mocks.map(route => {
|
||||
return responseFake(route.url, route.type, route.response)
|
||||
})
|
12
mock/mocks.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import user from './user'
|
||||
import role from './role'
|
||||
import article from './article'
|
||||
import search from './remoteSearch'
|
||||
|
||||
export default [
|
||||
...user,
|
||||
...role,
|
||||
...article,
|
||||
...search
|
||||
]
|
||||
|
51
mock/remoteSearch.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import Mock from 'mockjs'
|
||||
|
||||
const NameList = []
|
||||
const count = 100
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
NameList.push(Mock.mock({
|
||||
name: '@first'
|
||||
}))
|
||||
}
|
||||
NameList.push({ name: 'mock-Pan' })
|
||||
|
||||
export default [
|
||||
// username search
|
||||
{
|
||||
url: '/search/user',
|
||||
type: 'get',
|
||||
response: config => {
|
||||
const { name } = config.query
|
||||
const mockNameList = NameList.filter(item => {
|
||||
const lowerCaseName = item.name.toLowerCase()
|
||||
return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0)
|
||||
})
|
||||
return {
|
||||
code: 20000,
|
||||
data: { items: mockNameList }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// transaction list
|
||||
{
|
||||
url: '/transaction/list',
|
||||
type: 'get',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: {
|
||||
total: 20,
|
||||
'items|20': [{
|
||||
order_no: '@guid()',
|
||||
timestamp: +Mock.Random.date('T'),
|
||||
username: '@name()',
|
||||
price: '@float(1000, 15000, 0, 2)',
|
||||
'status|1': ['success', 'pending']
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
98
mock/role/index.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import Mock from 'mockjs'
|
||||
import { deepClone } from '../../src/utils/index.js'
|
||||
import { asyncRoutes, constantRoutes } from './routes.js'
|
||||
|
||||
const routes = deepClone([...constantRoutes, ...asyncRoutes])
|
||||
|
||||
const roles = [
|
||||
{
|
||||
key: 'admin',
|
||||
name: 'admin',
|
||||
description: 'Super Administrator. Have access to view all pages.',
|
||||
routes: routes
|
||||
},
|
||||
{
|
||||
key: 'editor',
|
||||
name: 'editor',
|
||||
description: 'Normal Editor. Can see all pages except permission page',
|
||||
routes: routes.filter(i => i.path !== '/permission')// just a mock
|
||||
},
|
||||
{
|
||||
key: 'visitor',
|
||||
name: 'visitor',
|
||||
description: 'Just a visitor. Can only see the home page and the document page',
|
||||
routes: [{
|
||||
path: '',
|
||||
redirect: 'dashboard',
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard', icon: 'dashboard' }
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
// mock get all routes form server
|
||||
{
|
||||
url: '/routes',
|
||||
type: 'get',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: routes
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// mock get all roles form server
|
||||
{
|
||||
url: '/roles',
|
||||
type: 'get',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: roles
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// add role
|
||||
{
|
||||
url: '/role',
|
||||
type: 'post',
|
||||
response: {
|
||||
code: 20000,
|
||||
data: {
|
||||
key: Mock.mock('@integer(300, 5000)')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// update role
|
||||
{
|
||||
url: '/role/[A-Za-z0-9]',
|
||||
type: 'put',
|
||||
response: {
|
||||
code: 20000,
|
||||
data: {
|
||||
status: 'success'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// delete role
|
||||
{
|
||||
url: '/role/[A-Za-z0-9]',
|
||||
type: 'delete',
|
||||
response: {
|
||||
code: 20000,
|
||||
data: {
|
||||
status: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
525
mock/role/routes.js
Normal file
@@ -0,0 +1,525 @@
|
||||
// Just a mock data
|
||||
|
||||
export const constantRoutes = [
|
||||
{
|
||||
path: '/redirect',
|
||||
component: 'layout/Layout',
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '/redirect/:path*',
|
||||
component: 'views/redirect/index'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
component: 'views/login/index',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/auth-redirect',
|
||||
component: 'views/login/authredirect',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: 'views/errorPage/404',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/401',
|
||||
component: 'views/errorPage/401',
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'dashboard',
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
component: 'views/dashboard/index',
|
||||
name: 'Dashboard',
|
||||
meta: { title: 'dashboard', icon: 'dashboard', noCache: true, affix: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/documentation',
|
||||
component: 'layout/Layout',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/documentation/index',
|
||||
name: 'Documentation',
|
||||
meta: { title: 'documentation', icon: 'documentation', affix: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/guide',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/guide/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/guide/index',
|
||||
name: 'Guide',
|
||||
meta: { title: 'guide', icon: 'guide', noCache: true }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export const asyncRoutes = [
|
||||
{
|
||||
path: '/permission',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/permission/index',
|
||||
alwaysShow: true,
|
||||
meta: {
|
||||
title: 'permission',
|
||||
icon: 'lock',
|
||||
roles: ['admin', 'editor']
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'page',
|
||||
component: 'views/permission/page',
|
||||
name: 'PagePermission',
|
||||
meta: {
|
||||
title: 'pagePermission',
|
||||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'directive',
|
||||
component: 'views/permission/directive',
|
||||
name: 'DirectivePermission',
|
||||
meta: {
|
||||
title: 'directivePermission'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: 'views/permission/role',
|
||||
name: 'RolePermission',
|
||||
meta: {
|
||||
title: 'rolePermission',
|
||||
roles: ['admin']
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/icon',
|
||||
component: 'layout/Layout',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/svg-icons/index',
|
||||
name: 'Icons',
|
||||
meta: { title: 'icons', icon: 'icon', noCache: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/components',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'noredirect',
|
||||
name: 'ComponentDemo',
|
||||
meta: {
|
||||
title: 'components',
|
||||
icon: 'component'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'tinymce',
|
||||
component: 'views/components-demo/tinymce',
|
||||
name: 'TinymceDemo',
|
||||
meta: { title: 'tinymce' }
|
||||
},
|
||||
{
|
||||
path: 'markdown',
|
||||
component: 'views/components-demo/markdown',
|
||||
name: 'MarkdownDemo',
|
||||
meta: { title: 'markdown' }
|
||||
},
|
||||
{
|
||||
path: 'json-editor',
|
||||
component: 'views/components-demo/jsonEditor',
|
||||
name: 'JsonEditorDemo',
|
||||
meta: { title: 'jsonEditor' }
|
||||
},
|
||||
{
|
||||
path: 'splitpane',
|
||||
component: 'views/components-demo/splitpane',
|
||||
name: 'SplitpaneDemo',
|
||||
meta: { title: 'splitPane' }
|
||||
},
|
||||
{
|
||||
path: 'avatar-upload',
|
||||
component: 'views/components-demo/avatarUpload',
|
||||
name: 'AvatarUploadDemo',
|
||||
meta: { title: 'avatarUpload' }
|
||||
},
|
||||
{
|
||||
path: 'dropzone',
|
||||
component: 'views/components-demo/dropzone',
|
||||
name: 'DropzoneDemo',
|
||||
meta: { title: 'dropzone' }
|
||||
},
|
||||
{
|
||||
path: 'sticky',
|
||||
component: 'views/components-demo/sticky',
|
||||
name: 'StickyDemo',
|
||||
meta: { title: 'sticky' }
|
||||
},
|
||||
{
|
||||
path: 'count-to',
|
||||
component: 'views/components-demo/countTo',
|
||||
name: 'CountToDemo',
|
||||
meta: { title: 'countTo' }
|
||||
},
|
||||
{
|
||||
path: 'mixin',
|
||||
component: 'views/components-demo/mixin',
|
||||
name: 'ComponentMixinDemo',
|
||||
meta: { title: 'componentMixin' }
|
||||
},
|
||||
{
|
||||
path: 'back-to-top',
|
||||
component: 'views/components-demo/backToTop',
|
||||
name: 'BackToTopDemo',
|
||||
meta: { title: 'backToTop' }
|
||||
},
|
||||
{
|
||||
path: 'drag-dialog',
|
||||
component: 'views/components-demo/dragDialog',
|
||||
name: 'DragDialogDemo',
|
||||
meta: { title: 'dragDialog' }
|
||||
},
|
||||
{
|
||||
path: 'drag-select',
|
||||
component: 'views/components-demo/dragSelect',
|
||||
name: 'DragSelectDemo',
|
||||
meta: { title: 'dragSelect' }
|
||||
},
|
||||
{
|
||||
path: 'dnd-list',
|
||||
component: 'views/components-demo/dndList',
|
||||
name: 'DndListDemo',
|
||||
meta: { title: 'dndList' }
|
||||
},
|
||||
{
|
||||
path: 'drag-kanban',
|
||||
component: 'views/components-demo/dragKanban',
|
||||
name: 'DragKanbanDemo',
|
||||
meta: { title: 'dragKanban' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/charts',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'noredirect',
|
||||
name: 'Charts',
|
||||
meta: {
|
||||
title: 'charts',
|
||||
icon: 'chart'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'keyboard',
|
||||
component: 'views/charts/keyboard',
|
||||
name: 'KeyboardChart',
|
||||
meta: { title: 'keyboardChart', noCache: true }
|
||||
},
|
||||
{
|
||||
path: 'line',
|
||||
component: 'views/charts/line',
|
||||
name: 'LineChart',
|
||||
meta: { title: 'lineChart', noCache: true }
|
||||
},
|
||||
{
|
||||
path: 'mixchart',
|
||||
component: 'views/charts/mixChart',
|
||||
name: 'MixChart',
|
||||
meta: { title: 'mixChart', noCache: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/nested',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/nested/menu1/menu1-1',
|
||||
name: 'Nested',
|
||||
meta: {
|
||||
title: 'nested',
|
||||
icon: 'nested'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1',
|
||||
component: 'views/nested/menu1/index',
|
||||
name: 'Menu1',
|
||||
meta: { title: 'menu1' },
|
||||
redirect: '/nested/menu1/menu1-1',
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1',
|
||||
component: 'views/nested/menu1/menu1-1',
|
||||
name: 'Menu1-1',
|
||||
meta: { title: 'menu1-1' }
|
||||
},
|
||||
{
|
||||
path: 'menu1-2',
|
||||
component: 'views/nested/menu1/menu1-2',
|
||||
name: 'Menu1-2',
|
||||
redirect: '/nested/menu1/menu1-2/menu1-2-1',
|
||||
meta: { title: 'menu1-2' },
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-2-1',
|
||||
component: 'views/nested/menu1/menu1-2/menu1-2-1',
|
||||
name: 'Menu1-2-1',
|
||||
meta: { title: 'menu1-2-1' }
|
||||
},
|
||||
{
|
||||
path: 'menu1-2-2',
|
||||
component: 'views/nested/menu1/menu1-2/menu1-2-2',
|
||||
name: 'Menu1-2-2',
|
||||
meta: { title: 'menu1-2-2' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu1-3',
|
||||
component: 'views/nested/menu1/menu1-3',
|
||||
name: 'Menu1-3',
|
||||
meta: { title: 'menu1-3' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu2',
|
||||
name: 'Menu2',
|
||||
component: 'views/nested/menu2/index',
|
||||
meta: { title: 'menu2' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/example',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/example/list',
|
||||
name: 'Example',
|
||||
meta: {
|
||||
title: 'example',
|
||||
icon: 'example'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'create',
|
||||
component: 'views/example/create',
|
||||
name: 'CreateArticle',
|
||||
meta: { title: 'createArticle', icon: 'edit' }
|
||||
},
|
||||
{
|
||||
path: 'edit/:id(\\d+)',
|
||||
component: 'views/example/edit',
|
||||
name: 'EditArticle',
|
||||
meta: { title: 'editArticle', noCache: true },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'list',
|
||||
component: 'views/example/list',
|
||||
name: 'ArticleList',
|
||||
meta: { title: 'articleList', icon: 'list' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/tab',
|
||||
component: 'layout/Layout',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/tab/index',
|
||||
name: 'Tab',
|
||||
meta: { title: 'tab', icon: 'tab' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/error',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'noredirect',
|
||||
name: 'ErrorPages',
|
||||
meta: {
|
||||
title: 'errorPages',
|
||||
icon: '404'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '401',
|
||||
component: 'views/errorPage/401',
|
||||
name: 'Page401',
|
||||
meta: { title: 'page401', noCache: true }
|
||||
},
|
||||
{
|
||||
path: '404',
|
||||
component: 'views/errorPage/404',
|
||||
name: 'Page404',
|
||||
meta: { title: 'page404', noCache: true }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/error-log',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'noredirect',
|
||||
children: [
|
||||
{
|
||||
path: 'log',
|
||||
component: 'views/errorLog/index',
|
||||
name: 'ErrorLog',
|
||||
meta: { title: 'errorLog', icon: 'bug' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/excel',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/excel/export-excel',
|
||||
name: 'Excel',
|
||||
meta: {
|
||||
title: 'excel',
|
||||
icon: 'excel'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'export-excel',
|
||||
component: 'views/excel/exportExcel',
|
||||
name: 'ExportExcel',
|
||||
meta: { title: 'exportExcel' }
|
||||
},
|
||||
{
|
||||
path: 'export-selected-excel',
|
||||
component: 'views/excel/selectExcel',
|
||||
name: 'SelectExcel',
|
||||
meta: { title: 'selectExcel' }
|
||||
},
|
||||
{
|
||||
path: 'export-merge-header',
|
||||
component: 'views/excel/mergeHeader',
|
||||
name: 'MergeHeader',
|
||||
meta: { title: 'mergeHeader' }
|
||||
},
|
||||
{
|
||||
path: 'upload-excel',
|
||||
component: 'views/excel/uploadExcel',
|
||||
name: 'UploadExcel',
|
||||
meta: { title: 'uploadExcel' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/zip',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/zip/download',
|
||||
alwaysShow: true,
|
||||
meta: { title: 'zip', icon: 'zip' },
|
||||
children: [
|
||||
{
|
||||
path: 'download',
|
||||
component: 'views/zip/index',
|
||||
name: 'ExportZip',
|
||||
meta: { title: 'exportZip' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/pdf',
|
||||
component: 'layout/Layout',
|
||||
redirect: '/pdf/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/pdf/index',
|
||||
name: 'PDF',
|
||||
meta: { title: 'pdf', icon: 'pdf' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/pdf/download',
|
||||
component: 'views/pdf/download',
|
||||
hidden: true
|
||||
},
|
||||
|
||||
{
|
||||
path: '/theme',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'noredirect',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/theme/index',
|
||||
name: 'Theme',
|
||||
meta: { title: 'theme', icon: 'theme' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/clipboard',
|
||||
component: 'layout/Layout',
|
||||
redirect: 'noredirect',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/clipboard/index',
|
||||
name: 'ClipboardDemo',
|
||||
meta: { title: 'clipboardDemo', icon: 'clipboard' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/i18n',
|
||||
component: 'layout/Layout',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
component: 'views/i18n-demo/index',
|
||||
name: 'I18n',
|
||||
meta: { title: 'i18n', icon: 'international' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: 'external-link',
|
||||
component: 'layout/Layout',
|
||||
children: [
|
||||
{
|
||||
path: 'https://github.com/PanJiaChen/vue-element-admin',
|
||||
meta: { title: 'externalLink', icon: 'link' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
64
mock/user.js
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
const tokens = {
|
||||
admin: {
|
||||
token: 'admin-token'
|
||||
},
|
||||
editor: {
|
||||
token: 'editor-token'
|
||||
}
|
||||
}
|
||||
|
||||
const users = {
|
||||
'admin-token': {
|
||||
roles: ['admin'],
|
||||
introduction: 'I am a super administrator',
|
||||
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
|
||||
name: 'Super Admin'
|
||||
},
|
||||
'editor-token': {
|
||||
roles: ['editor'],
|
||||
introduction: 'I am an editor',
|
||||
avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
|
||||
name: 'Normal Editor'
|
||||
}
|
||||
}
|
||||
|
||||
export default [
|
||||
// user login
|
||||
{
|
||||
url: '/user/login',
|
||||
type: 'post',
|
||||
response: config => {
|
||||
const { username } = config.body
|
||||
return {
|
||||
code: 20000,
|
||||
data: tokens[username]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// get user info
|
||||
{
|
||||
url: '/user/info\.*',
|
||||
type: 'get',
|
||||
response: config => {
|
||||
const { token } = config.query
|
||||
return {
|
||||
code: 20000,
|
||||
data: users[token]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// user logout
|
||||
{
|
||||
url: '/user/logout',
|
||||
type: 'post',
|
||||
response: _ => {
|
||||
return {
|
||||
code: 20000,
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
134
package.json
@@ -1,16 +1,25 @@
|
||||
{
|
||||
"name": "vue-element-admin",
|
||||
"version": "3.7.3",
|
||||
"version": "4.0.0",
|
||||
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
|
||||
"author": "Pan <panfree23@gmail.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
|
||||
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
|
||||
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
|
||||
"dev": "vue-cli-service serve",
|
||||
"build:prod": "vue-cli-service build",
|
||||
"build:stage": "vue-cli-service build --mode staging",
|
||||
"build:preview": "node build/index.js --preview",
|
||||
"build:report": "node build/index.js --report",
|
||||
"lint": "eslint --ext .js,.vue src",
|
||||
"test": "npm run lint",
|
||||
"precommit": "lint-staged"
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
|
||||
"new": "plop"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,vue}": [
|
||||
@@ -33,89 +42,64 @@
|
||||
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.17.1",
|
||||
"axios": "0.18.0",
|
||||
"clipboard": "1.7.1",
|
||||
"codemirror": "5.32.0",
|
||||
"connect": "3.6.6",
|
||||
"driver.js": "0.5.2",
|
||||
"dropzone": "5.2.0",
|
||||
"echarts": "3.8.5",
|
||||
"element-ui": "2.3.2",
|
||||
"file-saver": "1.3.3",
|
||||
"font-awesome": "4.7.0",
|
||||
"codemirror": "5.44.0",
|
||||
"driver.js": "0.9.5",
|
||||
"dropzone": "5.5.1",
|
||||
"echarts": "4.1.0",
|
||||
"element-ui": "2.6.1",
|
||||
"file-saver": "2.0.1",
|
||||
"fuse.js": "3.4.4",
|
||||
"js-cookie": "2.2.0",
|
||||
"jsonlint": "1.6.3",
|
||||
"jszip": "3.1.5",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"jszip": "3.2.0",
|
||||
"normalize.css": "7.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
"screenfull": "3.3.2",
|
||||
"serve-static": "1.13.2",
|
||||
"showdown": "1.8.5",
|
||||
"simplemde": "1.11.2",
|
||||
"sortablejs": "1.7.0",
|
||||
"vue": "2.5.10",
|
||||
"path-to-regexp": "2.4.0",
|
||||
"screenfull": "4.0.1",
|
||||
"showdown": "1.9.0",
|
||||
"sortablejs": "1.8.3",
|
||||
"tui-editor": "1.3.2",
|
||||
"vue": "2.6.8",
|
||||
"vue-count-to": "1.0.13",
|
||||
"vue-i18n": "7.3.2",
|
||||
"vue-multiselect": "2.0.8",
|
||||
"vue-router": "3.0.1",
|
||||
"vue-router": "3.0.2",
|
||||
"vue-splitpane": "1.0.2",
|
||||
"vuedraggable": "^2.16.0",
|
||||
"vuex": "3.0.1",
|
||||
"xlsx": "^0.11.16"
|
||||
"vuedraggable": "2.17.0",
|
||||
"vuex": "3.1.0",
|
||||
"xlsx": "0.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "7.2.3",
|
||||
"babel-core": "6.26.0",
|
||||
"babel-eslint": "8.0.3",
|
||||
"babel-helper-vue-jsx-merge-props": "2.0.3",
|
||||
"babel-loader": "7.1.2",
|
||||
"babel-plugin-dynamic-import-node": "^1.2.0",
|
||||
"babel-plugin-syntax-jsx": "6.18.0",
|
||||
"babel-plugin-transform-runtime": "6.23.0",
|
||||
"babel-plugin-transform-vue-jsx": "3.5.0",
|
||||
"babel-preset-env": "1.6.1",
|
||||
"babel-preset-stage-2": "6.24.1",
|
||||
"chalk": "2.3.0",
|
||||
"copy-webpack-plugin": "4.3.0",
|
||||
"cross-env": "5.1.1",
|
||||
"css-loader": "0.28.7",
|
||||
"eslint": "4.13.1",
|
||||
"eslint-friendly-formatter": "3.0.0",
|
||||
"eslint-loader": "1.9.0",
|
||||
"eslint-plugin-html": "4.0.1",
|
||||
"extract-text-webpack-plugin": "3.0.2",
|
||||
"file-loader": "1.1.5",
|
||||
"friendly-errors-webpack-plugin": "1.6.1",
|
||||
"html-webpack-plugin": "2.30.1",
|
||||
"husky": "0.14.3",
|
||||
"lint-staged": "7.2.0",
|
||||
"node-notifier": "5.1.2",
|
||||
"node-sass": "^4.7.2",
|
||||
"optimize-css-assets-webpack-plugin": "3.2.0",
|
||||
"ora": "1.3.0",
|
||||
"portfinder": "1.0.13",
|
||||
"postcss-import": "11.0.0",
|
||||
"postcss-loader": "2.0.9",
|
||||
"postcss-url": "7.3.0",
|
||||
"rimraf": "2.6.2",
|
||||
"sass-loader": "6.0.6",
|
||||
"@babel/core": "7.0.0",
|
||||
"@babel/register": "7.0.0",
|
||||
"@vue/cli-plugin-babel": "3.5.0",
|
||||
"@vue/cli-plugin-unit-jest": "3.5.0",
|
||||
"@vue/cli-service": "3.5.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-jest": "23.6.0",
|
||||
"chalk": "2.4.2",
|
||||
"connect": "3.6.6",
|
||||
"eslint": "5.15.1",
|
||||
"eslint-plugin-vue": "5.2.2",
|
||||
"husky": "1.3.1",
|
||||
"lint-staged": "7.2.2",
|
||||
"mockjs": "1.0.1-beta3",
|
||||
"node-sass": "^4.9.0",
|
||||
"plop": "2.3.0",
|
||||
"runjs": "^4.3.2",
|
||||
"sass-loader": "^7.1.0",
|
||||
"script-ext-html-webpack-plugin": "2.1.3",
|
||||
"script-loader": "0.7.2",
|
||||
"semver": "5.4.1",
|
||||
"shelljs": "0.7.8",
|
||||
"svg-sprite-loader": "3.5.2",
|
||||
"uglifyjs-webpack-plugin": "1.1.3",
|
||||
"url-loader": "0.6.2",
|
||||
"vue-loader": "13.7.2",
|
||||
"vue-style-loader": "3.0.3",
|
||||
"vue-template-compiler": "2.5.10",
|
||||
"webpack": "3.10.0",
|
||||
"webpack-bundle-analyzer": "2.9.1",
|
||||
"webpack-dev-server": "2.9.7",
|
||||
"webpack-merge": "4.1.1"
|
||||
"serve-static": "^1.13.2",
|
||||
"svg-sprite-loader": "4.1.3",
|
||||
"svgo": "1.2.0",
|
||||
"vue-template-compiler": "2.6.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0",
|
||||
"node": ">=8.9",
|
||||
"npm": ">= 3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
|
26
plop-templates/component/index.hbs
Normal file
@@ -0,0 +1,26 @@
|
||||
{{#if template}}
|
||||
<template>
|
||||
<div />
|
||||
</template>
|
||||
{{/if}}
|
||||
|
||||
{{#if script}}
|
||||
<script>
|
||||
export default {
|
||||
name: '{{ properCase name }}',
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if style}}
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
{{/if}}
|
55
plop-templates/component/prompt.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const { notEmpty } = require('../utils.js')
|
||||
|
||||
module.exports = {
|
||||
description: 'generate vue component',
|
||||
prompts: [{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: 'component name please',
|
||||
validate: notEmpty('name')
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'blocks',
|
||||
message: 'Blocks:',
|
||||
choices: [{
|
||||
name: '<template>',
|
||||
value: 'template',
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
name: '<script>',
|
||||
value: 'script',
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
name: 'style',
|
||||
value: 'style',
|
||||
checked: true
|
||||
}
|
||||
],
|
||||
validate(value) {
|
||||
if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
|
||||
return 'Components require at least a <script> or <template> tag.'
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
],
|
||||
actions: data => {
|
||||
const name = '{{properCase name}}'
|
||||
const actions = [{
|
||||
type: 'add',
|
||||
path: `src/components/${name}/index.vue`,
|
||||
templateFile: 'plop-templates/component/index.hbs',
|
||||
data: {
|
||||
name: name,
|
||||
template: data.blocks.includes('template'),
|
||||
script: data.blocks.includes('script'),
|
||||
style: data.blocks.includes('style')
|
||||
}
|
||||
}]
|
||||
|
||||
return actions
|
||||
}
|
||||
}
|
9
plop-templates/utils.js
Normal file
@@ -0,0 +1,9 @@
|
||||
exports.notEmpty = name => {
|
||||
return v => {
|
||||
if (!v || v.trim === '') {
|
||||
return `${name} is required`
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
26
plop-templates/view/index.hbs
Normal file
@@ -0,0 +1,26 @@
|
||||
{{#if template}}
|
||||
<template>
|
||||
<div />
|
||||
</template>
|
||||
{{/if}}
|
||||
|
||||
{{#if script}}
|
||||
<script>
|
||||
export default {
|
||||
name: '{{ properCase name }}',
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
{{/if}}
|
||||
|
||||
{{#if style}}
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
{{/if}}
|
55
plop-templates/view/prompt.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const { notEmpty } = require('../utils.js')
|
||||
|
||||
module.exports = {
|
||||
description: 'generate a view',
|
||||
prompts: [{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: 'view name please',
|
||||
validate: notEmpty('name')
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'blocks',
|
||||
message: 'Blocks:',
|
||||
choices: [{
|
||||
name: '<template>',
|
||||
value: 'template',
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
name: '<script>',
|
||||
value: 'script',
|
||||
checked: true
|
||||
},
|
||||
{
|
||||
name: 'style',
|
||||
value: 'style',
|
||||
checked: true
|
||||
}
|
||||
],
|
||||
validate(value) {
|
||||
if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
|
||||
return 'View require at least a <script> or <template> tag.'
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
],
|
||||
actions: data => {
|
||||
const name = '{{name}}'
|
||||
const actions = [{
|
||||
type: 'add',
|
||||
path: `src/views/${name}/index.vue`,
|
||||
templateFile: 'plop-templates/view/index.hbs',
|
||||
data: {
|
||||
name: name,
|
||||
template: data.blocks.includes('template'),
|
||||
script: data.blocks.includes('script'),
|
||||
style: data.blocks.includes('style')
|
||||
}
|
||||
}]
|
||||
|
||||
return actions
|
||||
}
|
||||
}
|
7
plopfile.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const viewGenerator = require('./plop-templates/view/prompt')
|
||||
const componentGenerator = require('./plop-templates/component/prompt')
|
||||
|
||||
module.exports = function(plop) {
|
||||
plop.setGenerator('view', viewGenerator)
|
||||
plop.setGenerator('component', componentGenerator)
|
||||
}
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
@@ -5,10 +5,11 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<title>vue-element-admin</title>
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
</head>
|
||||
<body>
|
||||
<script src=<%= htmlWebpackPlugin.options.path %>/tinymce4.7.5/tinymce.min.js></script>
|
||||
<script src="<%= BASE_URL %>static/tinymce4.7.5/tinymce.min.js"></script>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 329 B After Width: | Height: | Size: 329 B |
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 331 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 342 B |
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 343 B After Width: | Height: | Size: 343 B |
Before Width: | Height: | Size: 321 B After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 323 B After Width: | Height: | Size: 323 B |
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 337 B After Width: | Height: | Size: 337 B |
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
Before Width: | Height: | Size: 336 B After Width: | Height: | Size: 336 B |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 53 B After Width: | Height: | Size: 53 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 152 B After Width: | Height: | Size: 152 B |
Before Width: | Height: | Size: 43 B After Width: | Height: | Size: 43 B |
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default{
|
||||
name: 'App'
|
||||
}
|
||||
export default {
|
||||
name: 'App'
|
||||
}
|
||||
</script>
|
||||
|
@@ -7,3 +7,11 @@ export function userSearch(name) {
|
||||
params: { name }
|
||||
})
|
||||
}
|
||||
|
||||
export function transactionList(query) {
|
||||
return request({
|
||||
url: '/transaction/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
38
src/api/role.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function getRoutes() {
|
||||
return request({
|
||||
url: '/routes',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function getRoles() {
|
||||
return request({
|
||||
url: '/roles',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addRole(data) {
|
||||
return request({
|
||||
url: '/role',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function updateRole(id, data) {
|
||||
return request({
|
||||
url: `/role/${id}`,
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteRole(id) {
|
||||
return request({
|
||||
url: `/role/${id}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function fetchList(query) {
|
||||
return request({
|
||||
url: '/transaction/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
@@ -1,25 +1,14 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function loginByUsername(username, password) {
|
||||
const data = {
|
||||
username,
|
||||
password
|
||||
}
|
||||
export function login(data) {
|
||||
return request({
|
||||
url: '/login/login',
|
||||
url: '/user/login',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/login/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function getUserInfo(token) {
|
||||
export function getInfo(token) {
|
||||
return request({
|
||||
url: '/user/info',
|
||||
method: 'get',
|
||||
@@ -27,3 +16,10 @@ export function getUserInfo(token) {
|
||||
})
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/user/logout',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
@@ -1,199 +0,0 @@
|
||||
/* eslint-disable */
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['exports', 'echarts'], factory);
|
||||
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
|
||||
// CommonJS
|
||||
factory(exports, require('echarts'));
|
||||
} else {
|
||||
// Browser globals
|
||||
factory({}, root.echarts);
|
||||
}
|
||||
}(this, function (exports, echarts) {
|
||||
var log = function (msg) {
|
||||
if (typeof console !== 'undefined') {
|
||||
console && console.error && console.error(msg);
|
||||
}
|
||||
};
|
||||
if (!echarts) {
|
||||
log('ECharts is not Loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
var colorPalette = [
|
||||
'#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80',
|
||||
'#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
|
||||
'#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
|
||||
'#59678c','#c9ab00','#7eb00a','#6f5553','#c14089'
|
||||
];
|
||||
|
||||
|
||||
var theme = {
|
||||
color: colorPalette,
|
||||
|
||||
title: {
|
||||
textStyle: {
|
||||
fontWeight: 'normal',
|
||||
color: '#008acd'
|
||||
}
|
||||
},
|
||||
|
||||
visualMap: {
|
||||
itemWidth: 15,
|
||||
color: ['#5ab1ef','#e0ffff']
|
||||
},
|
||||
|
||||
toolbox: {
|
||||
iconStyle: {
|
||||
normal: {
|
||||
borderColor: colorPalette[0]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(50,50,50,0.5)',
|
||||
axisPointer : {
|
||||
type : 'line',
|
||||
lineStyle : {
|
||||
color: '#008acd'
|
||||
},
|
||||
crossStyle: {
|
||||
color: '#008acd'
|
||||
},
|
||||
shadowStyle : {
|
||||
color: 'rgba(200,200,200,0.2)'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
dataZoom: {
|
||||
dataBackgroundColor: '#efefff',
|
||||
fillerColor: 'rgba(182,162,222,0.2)',
|
||||
handleColor: '#008acd'
|
||||
},
|
||||
|
||||
grid: {
|
||||
borderColor: '#eee'
|
||||
},
|
||||
|
||||
categoryAxis: {
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#008acd'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: ['#eee']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
valueAxis: {
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#008acd'
|
||||
}
|
||||
},
|
||||
splitArea : {
|
||||
show : true,
|
||||
areaStyle : {
|
||||
color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)']
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: ['#eee']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
timeline : {
|
||||
lineStyle : {
|
||||
color : '#008acd'
|
||||
},
|
||||
controlStyle : {
|
||||
normal : { color : '#008acd'},
|
||||
emphasis : { color : '#008acd'}
|
||||
},
|
||||
symbol : 'emptyCircle',
|
||||
symbolSize : 3
|
||||
},
|
||||
|
||||
line: {
|
||||
smooth : true,
|
||||
symbol: 'emptyCircle',
|
||||
symbolSize: 3
|
||||
},
|
||||
|
||||
candlestick: {
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: '#d87a80',
|
||||
color0: '#2ec7c9',
|
||||
lineStyle: {
|
||||
color: '#d87a80',
|
||||
color0: '#2ec7c9'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
scatter: {
|
||||
symbol: 'circle',
|
||||
symbolSize: 4
|
||||
},
|
||||
|
||||
map: {
|
||||
label: {
|
||||
normal: {
|
||||
textStyle: {
|
||||
color: '#d87a80'
|
||||
}
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
borderColor: '#eee',
|
||||
areaColor: '#ddd'
|
||||
},
|
||||
emphasis: {
|
||||
areaColor: '#fe994e'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
graph: {
|
||||
color: colorPalette
|
||||
},
|
||||
|
||||
gauge : {
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']],
|
||||
width: 10
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
splitNumber: 10,
|
||||
length :15,
|
||||
lineStyle: {
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
length :22,
|
||||
lineStyle: {
|
||||
color: 'auto'
|
||||
}
|
||||
},
|
||||
pointer : {
|
||||
width : 5
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
echarts.registerTheme('macarons', theme);
|
||||
}));
|
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<transition :name="transitionName">
|
||||
<div class="back-to-ceiling" @click="backToTop" v-show="visible" :style="customStyle">
|
||||
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
|
||||
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height: 16px; width: 16px;">
|
||||
<title>回到顶部</title>
|
||||
<g>
|
||||
<path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd"></path>
|
||||
<path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -45,7 +45,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
interval: null
|
||||
interval: null,
|
||||
isMoving: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -62,13 +63,16 @@ export default {
|
||||
this.visible = window.pageYOffset > this.visibilityHeight
|
||||
},
|
||||
backToTop() {
|
||||
if (this.isMoving) return
|
||||
const start = window.pageYOffset
|
||||
let i = 0
|
||||
this.isMoving = true
|
||||
this.interval = setInterval(() => {
|
||||
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
|
||||
if (next <= this.backPosition) {
|
||||
window.scrollTo(0, this.backPosition)
|
||||
clearInterval(this.interval)
|
||||
this.isMoving = false
|
||||
} else {
|
||||
window.scrollTo(0, next)
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if='item.meta.title'>
|
||||
<span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{generateTitle(item.meta.title)}}</span>
|
||||
<router-link v-else :to="item.redirect||item.path">{{generateTitle(item.meta.title)}}</router-link>
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{
|
||||
generateTitle(item.meta.title) }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
@@ -11,11 +12,9 @@
|
||||
|
||||
<script>
|
||||
import { generateTitle } from '@/utils/i18n'
|
||||
import pathToRegexp from 'path-to-regexp'
|
||||
|
||||
export default {
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
levelList: null
|
||||
@@ -26,15 +25,34 @@ export default {
|
||||
this.getBreadcrumb()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getBreadcrumb()
|
||||
},
|
||||
methods: {
|
||||
generateTitle,
|
||||
getBreadcrumb() {
|
||||
let matched = this.$route.matched.filter(item => item.name)
|
||||
|
||||
const first = matched[0]
|
||||
if (first && first.name !== 'dashboard') {
|
||||
if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) {
|
||||
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
|
||||
}
|
||||
this.levelList = matched
|
||||
|
||||
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
},
|
||||
pathCompile(path) {
|
||||
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
|
||||
const { params } = this.$route
|
||||
var toPath = pathToRegexp.compile(path)
|
||||
return toPath(params)
|
||||
},
|
||||
handleLink(item) {
|
||||
const { redirect, path } = item
|
||||
if (redirect) {
|
||||
this.$router.push(redirect)
|
||||
return
|
||||
}
|
||||
this.$router.push(this.pathCompile(path))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +63,7 @@ export default {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 10px;
|
||||
margin-left: 8px;
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -56,6 +56,10 @@ export default {
|
||||
this.chart.setOption(
|
||||
{
|
||||
backgroundColor: '#08263a',
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%'
|
||||
},
|
||||
xAxis: [{
|
||||
show: false,
|
||||
data: xAxisData
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -80,8 +80,8 @@ export default {
|
||||
},
|
||||
grid: {
|
||||
top: 100,
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
left: '2%',
|
||||
right: '2%',
|
||||
bottom: '2%',
|
||||
containLabel: true
|
||||
},
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="className" :id="id" :style="{height:height,width:width}"></div>
|
||||
<div :id="id" :class="className" :style="{height:height,width:width}" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -75,8 +75,10 @@ export default {
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
borderWidth: 0,
|
||||
top: 110,
|
||||
top: 150,
|
||||
bottom: 95,
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
|
@@ -1,15 +1,32 @@
|
||||
import { debounce } from '@/utils'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
sidebarElm: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.__resizeHanlder = debounce(() => {
|
||||
this.__resizeHandler = debounce(() => {
|
||||
if (this.chart) {
|
||||
this.chart.resize()
|
||||
}
|
||||
}, 100)
|
||||
window.addEventListener('resize', this.__resizeHanlder)
|
||||
window.addEventListener('resize', this.__resizeHandler)
|
||||
|
||||
this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
||||
this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.__resizeHanlder)
|
||||
window.removeEventListener('resize', this.__resizeHandler)
|
||||
|
||||
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
|
||||
},
|
||||
methods: {
|
||||
sidebarResizeHandler(e) {
|
||||
if (e.propertyName === 'width') {
|
||||
this.__resizeHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,23 +1,27 @@
|
||||
<template>
|
||||
<div class="dndList">
|
||||
<div class="dndList-list" :style="{width:width1}">
|
||||
<h3>{{list1Title}}</h3>
|
||||
<draggable :list="list1" class="dragArea" :options="{group:'article'}">
|
||||
<div class="list-complete-item" v-for="element in list1" :key='element.id'>
|
||||
<div class="list-complete-item-handle">[{{element.author}}] {{element.title}}</div>
|
||||
<div :style="{width:width1}" class="dndList-list">
|
||||
<h3>{{ list1Title }}</h3>
|
||||
<draggable :list="list1" :options="{group:'article'}" class="dragArea">
|
||||
<div v-for="element in list1" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle">
|
||||
{{ element.id }}[{{ element.author }}] {{ element.title }}
|
||||
</div>
|
||||
<div style="position:absolute;right:0px;">
|
||||
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
|
||||
<i style="color:#ff4949" class="el-icon-delete"></i>
|
||||
<i style="color:#ff4949" class="el-icon-delete" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="dndList-list" :style="{width:width2}">
|
||||
<h3>{{list2Title}}</h3>
|
||||
<draggable :list="filterList2" class="dragArea" :options="{group:'article'}">
|
||||
<div class="list-complete-item" v-for="element in filterList2" :key='element.id'>
|
||||
<div class='list-complete-item-handle2' @click="pushEle(element)"> [{{element.author}}] {{element.title}}</div>
|
||||
<div :style="{width:width2}" class="dndList-list">
|
||||
<h3>{{ list2Title }}</h3>
|
||||
<draggable :list="list2" :options="{group:'article'}" class="dragArea">
|
||||
<div v-for="element in list2" :key="element.id" class="list-complete-item">
|
||||
<div class="list-complete-item-handle2" @click="pushEle(element)">
|
||||
{{ element.id }} [{{ element.author }}] {{ element.title }}
|
||||
</div>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
@@ -30,16 +34,6 @@ import draggable from 'vuedraggable'
|
||||
export default {
|
||||
name: 'DndList',
|
||||
components: { draggable },
|
||||
computed: {
|
||||
filterList2() {
|
||||
return this.list2.filter(v => {
|
||||
if (this.isNotInList1(v)) {
|
||||
return v
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
},
|
||||
props: {
|
||||
list1: {
|
||||
type: Array,
|
||||
@@ -90,7 +84,16 @@ export default {
|
||||
}
|
||||
},
|
||||
pushEle(ele) {
|
||||
this.list1.push(ele)
|
||||
for (const item of this.list2) {
|
||||
if (item.id === ele.id) {
|
||||
const index = this.list2.indexOf(item)
|
||||
this.list2.splice(index, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (this.isNotInList1(ele)) {
|
||||
this.list1.push(ele)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
61
src/components/DragSelect/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners">
|
||||
<slot />
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Sortable from 'sortablejs'
|
||||
|
||||
export default {
|
||||
name: 'DragSelect',
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectVal: {
|
||||
get() {
|
||||
return [...this.value]
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('input', [...val])
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setSort()
|
||||
},
|
||||
methods: {
|
||||
setSort() {
|
||||
const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0]
|
||||
this.sortable = Sortable.create(el, {
|
||||
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
|
||||
setData: function(dataTransfer) {
|
||||
dataTransfer.setData('Text', '')
|
||||
// to avoid Firefox bug
|
||||
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
|
||||
},
|
||||
onEnd: evt => {
|
||||
const targetRow = this.value.splice(evt.oldIndex, 1)[0]
|
||||
this.value.splice(evt.newIndex, 0, targetRow)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drag-select >>> .sortable-ghost{
|
||||
opacity: .8;
|
||||
color: #fff!important;
|
||||
background: #42b983!important;
|
||||
}
|
||||
|
||||
.drag-select >>> .el-tag{
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :ref="id" :action="url" class="dropzone" :id="id">
|
||||
<div :id="id" :ref="id" :action="url" class="dropzone">
|
||||
<input type="file" name="file">
|
||||
</div>
|
||||
</template>
|
||||
@@ -12,12 +12,81 @@ import 'dropzone/dist/dropzone.css'
|
||||
Dropzone.autoDiscover = false
|
||||
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clickable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
defaultMsg: {
|
||||
type: String,
|
||||
default: '上传图片'
|
||||
},
|
||||
acceptedFiles: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
thumbnailHeight: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
thumbnailWidth: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
showRemoveLink: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maxFilesize: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
maxFiles: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
autoProcessQueue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useCustomDropzoneOptions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultImg: {
|
||||
default: '',
|
||||
type: [String, Array]
|
||||
},
|
||||
couldPaste: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dropzone: '',
|
||||
initOnce: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
defaultImg(val) {
|
||||
if (val.length === 0) {
|
||||
this.initOnce = false
|
||||
return
|
||||
}
|
||||
if (!this.initOnce) return
|
||||
this.initImages(val)
|
||||
this.initOnce = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const element = document.getElementById(this.id)
|
||||
const vm = this
|
||||
@@ -95,6 +164,10 @@ export default {
|
||||
vm.$emit('dropzone-successmultiple', file, error, xhr)
|
||||
})
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('paste', this.pasteImg)
|
||||
this.dropzone.destroy()
|
||||
},
|
||||
methods: {
|
||||
removeAllFiles() {
|
||||
this.dropzone.removeAllFiles(true)
|
||||
@@ -128,76 +201,6 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
destroyed() {
|
||||
document.removeEventListener('paste', this.pasteImg)
|
||||
this.dropzone.destroy()
|
||||
},
|
||||
watch: {
|
||||
defaultImg(val) {
|
||||
if (val.length === 0) {
|
||||
this.initOnce = false
|
||||
return
|
||||
}
|
||||
if (!this.initOnce) return
|
||||
this.initImages(val)
|
||||
this.initOnce = false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clickable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
defaultMsg: {
|
||||
type: String,
|
||||
default: '上传图片'
|
||||
},
|
||||
acceptedFiles: {
|
||||
type: String
|
||||
},
|
||||
thumbnailHeight: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
thumbnailWidth: {
|
||||
type: Number,
|
||||
default: 200
|
||||
},
|
||||
showRemoveLink: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maxFilesize: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
maxFiles: {
|
||||
type: Number,
|
||||
default: 3
|
||||
},
|
||||
autoProcessQueue: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useCustomDropzoneOptions: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultImg: {
|
||||
default: false
|
||||
},
|
||||
couldPaste: {
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@@ -1,49 +1,50 @@
|
||||
<template>
|
||||
<div v-if="errorLogs.length>0">
|
||||
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
|
||||
<el-button size="small" type="danger" class="bug-btn">
|
||||
<svg t="1492682037685" class="bug-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1863"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128">
|
||||
<path d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
|
||||
p-id="1864"></path>
|
||||
</svg>
|
||||
<el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true">
|
||||
<el-button style="padding: 8px 10px;" size="small" type="danger">
|
||||
<svg-icon icon-class="bug" />
|
||||
</el-button>
|
||||
</el-badge>
|
||||
|
||||
<el-dialog title="Error Log" :visible.sync="dialogTableVisible" width="80%">
|
||||
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%">
|
||||
<el-table :data="errorLogs" border>
|
||||
<el-table-column label="Message">
|
||||
<template slot-scope="scope">
|
||||
<div>
|
||||
<span class="message-title">Msg:</span>
|
||||
<el-tag type="danger">{{ scope.row.err.message }}</el-tag>
|
||||
<el-tag type="danger">
|
||||
{{ scope.row.err.message }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<br/>
|
||||
<br>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 10px;">Info: </span>
|
||||
<el-tag type="warning">{{scope.row.vm.$vnode.tag}} error in {{scope.row.info}}</el-tag>
|
||||
<el-tag type="warning">
|
||||
{{ scope.row.vm.$vnode.tag }} error in {{ scope.row.info }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<br/>
|
||||
<br>
|
||||
<div>
|
||||
<span class="message-title" style="padding-right: 16px;">Url: </span>
|
||||
<el-tag type="success">{{scope.row.url}}</el-tag>
|
||||
<el-tag type="success">
|
||||
{{ scope.row.url }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Stack">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.err.stack}}
|
||||
{{ scope.row.err.stack }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'errorLog',
|
||||
name: 'ErrorLog',
|
||||
data() {
|
||||
return {
|
||||
dialogTableVisible: false
|
||||
@@ -58,16 +59,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bug-btn.el-button--small {
|
||||
padding: 9px 10px;
|
||||
}
|
||||
.bug-svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
.message-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
|
@@ -1,12 +1,24 @@
|
||||
<template>
|
||||
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
|
||||
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#40c9c6; color:#fff;"
|
||||
aria-hidden="true">
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
||||
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor" class="octo-body"></path>
|
||||
<svg
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
style="fill:#40c9c6; color:#fff;"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
<path
|
||||
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor"
|
||||
style="transform-origin: 130px 106px;"
|
||||
class="octo-arm"
|
||||
/>
|
||||
<path
|
||||
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor"
|
||||
class="octo-body"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
|
@@ -1,28 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg t="1492500959545" @click="toggleClick" class="hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
|
||||
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
|
||||
<path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
|
||||
p-id="1692"></path>
|
||||
<path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
|
||||
p-id="1693"></path>
|
||||
<path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
|
||||
p-id="1694"></path>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg
|
||||
:class="{'is-active':isActive}"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'hamburger',
|
||||
name: 'Hamburger',
|
||||
props: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
toggleClick: {
|
||||
type: Function,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleClick() {
|
||||
this.$emit('toggleClick')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,15 +33,12 @@ export default {
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transform: rotate(90deg);
|
||||
transition: .38s;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(0deg);
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
190
src/components/HeaderSearch/index.vue
Normal file
@@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div :class="{'show':show}" class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||
<el-select
|
||||
ref="headerSearchSelect"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="Search"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// fuse is a lightweight fuzzy-search module
|
||||
// make search results more in line with expectations
|
||||
import Fuse from 'fuse.js'
|
||||
import path from 'path'
|
||||
import i18n from '@/lang'
|
||||
|
||||
export default {
|
||||
name: 'HeaderSearch',
|
||||
data() {
|
||||
return {
|
||||
search: '',
|
||||
options: [],
|
||||
searchPool: [],
|
||||
show: false,
|
||||
fuse: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
routes() {
|
||||
return this.$store.getters.permission_routes
|
||||
},
|
||||
lang() {
|
||||
return this.$store.getters.language
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
lang() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
routes() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
searchPool(list) {
|
||||
this.initFuse(list)
|
||||
},
|
||||
show(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.close)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.close)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.searchPool = this.generateRoutes(this.routes)
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
this.show = !this.show
|
||||
if (this.show) {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
||||
this.options = []
|
||||
this.show = false
|
||||
},
|
||||
change(val) {
|
||||
this.$router.push(val.path)
|
||||
this.search = ''
|
||||
this.options = []
|
||||
this.$nextTick(() => {
|
||||
this.show = false
|
||||
})
|
||||
},
|
||||
initFuse(list) {
|
||||
this.fuse = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
maxPatternLength: 32,
|
||||
minMatchCharLength: 1,
|
||||
keys: [{
|
||||
name: 'title',
|
||||
weight: 0.7
|
||||
}, {
|
||||
name: 'path',
|
||||
weight: 0.3
|
||||
}]
|
||||
})
|
||||
},
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
||||
let res = []
|
||||
|
||||
for (const router of routes) {
|
||||
// skip hidden router
|
||||
if (router.hidden) { continue }
|
||||
|
||||
const data = {
|
||||
path: path.resolve(basePath, router.path),
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
|
||||
if (router.meta && router.meta.title) {
|
||||
// generate internationalized title
|
||||
const i18ntitle = i18n.t(`route.${router.meta.title}`)
|
||||
|
||||
data.title = [...data.title, i18ntitle]
|
||||
|
||||
if (router.redirect !== 'noredirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (router.children) {
|
||||
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
querySearch(query) {
|
||||
if (query !== '') {
|
||||
this.options = this.fuse.search(query)
|
||||
} else {
|
||||
this.options = []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
/deep/ .el-input__inner {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 210px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|