@@ -0,0 +1,14 @@ | |||||
# https://editorconfig.org | |||||
root = true | |||||
[*] | |||||
charset = utf-8 | |||||
indent_style = space | |||||
indent_size = 2 | |||||
end_of_line = lf | |||||
insert_final_newline = true | |||||
trim_trailing_whitespace = true | |||||
[*.md] | |||||
insert_final_newline = false | |||||
trim_trailing_whitespace = false |
@@ -0,0 +1,7 @@ | |||||
# just a flag | |||||
ENV = 'development' | |||||
# base api | |||||
VUE_APP_BASE_URL = '/api' | |||||
VUE_APP_BASE_API = '/api' | |||||
IMAGE_BASE_PATH = 'http://192.168.0.116:3200/static' |
@@ -0,0 +1,7 @@ | |||||
# just a flag | |||||
ENV = 'production' | |||||
# base api | |||||
BASE_API = '' | |||||
VUE_APP_BASE_API = '' | |||||
@@ -0,0 +1,9 @@ | |||||
NODE_ENV = production | |||||
# just a flag | |||||
ENV = 'staging' | |||||
# base api | |||||
BASE_API = '/api' | |||||
VUE_APP_BASE_API = '/stage-api' | |||||
@@ -0,0 +1,4 @@ | |||||
build/*.js | |||||
src/assets | |||||
public | |||||
dist |
@@ -0,0 +1,276 @@ | |||||
module.exports = { | |||||
root: true, | |||||
parserOptions: { | |||||
parser: 'babel-eslint', | |||||
sourceType: 'module' | |||||
}, | |||||
env: { | |||||
browser: true, | |||||
node: true, | |||||
es6: true | |||||
}, | |||||
extends: ['plugin:vue/recommended', 'eslint:recommended'], | |||||
// add your custom rules here | |||||
//it is base on https://github.com/vuejs/eslint-config-vue | |||||
rules: { | |||||
'vue/max-attributes-per-line': 'off', | |||||
'vue/html-closing-bracket-newline': [ | |||||
0, | |||||
{ | |||||
singleline: 'never', | |||||
multiline: 'always' | |||||
} | |||||
], | |||||
'vue/html-indent': 'off', | |||||
indent: [2, 4], | |||||
'vue/html-self-closing': 'off', | |||||
'vue/singleline-html-element-content-newline': 'off', | |||||
'vue/multiline-html-element-content-newline': 'off', | |||||
'vue/name-property-casing': ['error', 'PascalCase'], | |||||
'vue/no-v-html': 'off', | |||||
'accessor-pairs': 2, | |||||
'arrow-spacing': [ | |||||
2, | |||||
{ | |||||
before: true, | |||||
after: true | |||||
} | |||||
], | |||||
'block-spacing': [2, 'always'], | |||||
'brace-style': [ | |||||
2, | |||||
'1tbs', | |||||
{ | |||||
allowSingleLine: true | |||||
} | |||||
], | |||||
camelcase: [ | |||||
0, | |||||
{ | |||||
properties: 'always' | |||||
} | |||||
], | |||||
'comma-dangle': [2, 'never'], | |||||
'comma-spacing': [ | |||||
2, | |||||
{ | |||||
before: false, | |||||
after: true | |||||
} | |||||
], | |||||
'comma-style': [2, 'last'], | |||||
'constructor-super': 2, | |||||
curly: [2, 'multi-line'], | |||||
'dot-location': [2, 'property'], | |||||
'eol-last': 2, | |||||
eqeqeq: ['error', 'always', { null: 'ignore' }], | |||||
'generator-star-spacing': [ | |||||
2, | |||||
{ | |||||
before: true, | |||||
after: true | |||||
} | |||||
], | |||||
'handle-callback-err': [2, '^(err|error)$'], | |||||
indent: [ | |||||
2, | |||||
2, | |||||
{ | |||||
SwitchCase: 1 | |||||
} | |||||
], | |||||
'jsx-quotes': [2, 'prefer-single'], | |||||
'key-spacing': [ | |||||
2, | |||||
{ | |||||
beforeColon: false, | |||||
afterColon: true | |||||
} | |||||
], | |||||
'keyword-spacing': [ | |||||
2, | |||||
{ | |||||
before: true, | |||||
after: true | |||||
} | |||||
], | |||||
'new-cap': [ | |||||
2, | |||||
{ | |||||
newIsCap: true, | |||||
capIsNew: false | |||||
} | |||||
], | |||||
'new-parens': 2, | |||||
'no-array-constructor': 2, | |||||
'no-caller': 2, | |||||
'no-console': 'off', | |||||
'no-class-assign': 2, | |||||
'no-cond-assign': 2, | |||||
'no-const-assign': 2, | |||||
'no-control-regex': 0, | |||||
'no-delete-var': 2, | |||||
'no-dupe-args': 2, | |||||
'no-dupe-class-members': 2, | |||||
'no-dupe-keys': 2, | |||||
'no-duplicate-case': 2, | |||||
'no-empty-character-class': 2, | |||||
'no-empty-pattern': 2, | |||||
'no-eval': 2, | |||||
'no-ex-assign': 2, | |||||
'no-extend-native': 2, | |||||
'no-extra-bind': 2, | |||||
'no-extra-boolean-cast': 2, | |||||
'no-extra-parens': [2, 'functions'], | |||||
'no-fallthrough': 2, | |||||
'no-floating-decimal': 2, | |||||
'no-func-assign': 2, | |||||
'no-implied-eval': 2, | |||||
'no-inner-declarations': [2, 'functions'], | |||||
'no-invalid-regexp': 2, | |||||
'no-irregular-whitespace': 2, | |||||
'no-iterator': 2, | |||||
'no-label-var': 2, | |||||
'no-labels': [ | |||||
2, | |||||
{ | |||||
allowLoop: false, | |||||
allowSwitch: false | |||||
} | |||||
], | |||||
'no-lone-blocks': 2, | |||||
'no-mixed-spaces-and-tabs': 2, | |||||
'no-multi-spaces': 2, | |||||
'no-multi-str': 2, | |||||
'no-multiple-empty-lines': [ | |||||
2, | |||||
{ | |||||
max: 1 | |||||
} | |||||
], | |||||
'no-native-reassign': 2, | |||||
'no-negated-in-lhs': 2, | |||||
'no-new-object': 2, | |||||
'no-new-require': 2, | |||||
'no-new-symbol': 2, | |||||
'no-new-wrappers': 2, | |||||
'no-obj-calls': 2, | |||||
'no-octal': 2, | |||||
'no-octal-escape': 2, | |||||
'no-path-concat': 2, | |||||
'no-proto': 2, | |||||
'no-redeclare': 2, | |||||
'no-regex-spaces': 2, | |||||
'no-return-assign': [2, 'except-parens'], | |||||
'no-self-assign': 2, | |||||
'no-self-compare': 2, | |||||
'no-sequences': 2, | |||||
'no-shadow-restricted-names': 2, | |||||
'no-spaced-func': 2, | |||||
'no-sparse-arrays': 2, | |||||
'no-this-before-super': 2, | |||||
'no-throw-literal': 2, | |||||
'no-trailing-spaces': 2, | |||||
'no-undef': 2, | |||||
'no-undef-init': 2, | |||||
'no-unexpected-multiline': 2, | |||||
'no-unmodified-loop-condition': 2, | |||||
'no-unneeded-ternary': [ | |||||
2, | |||||
{ | |||||
defaultAssignment: false | |||||
} | |||||
], | |||||
'no-unreachable': 2, | |||||
'no-unsafe-finally': 2, | |||||
'no-unused-vars': [ | |||||
2, | |||||
{ | |||||
vars: 'all', | |||||
args: 'none' | |||||
} | |||||
], | |||||
'no-useless-call': 2, | |||||
'no-useless-computed-key': 2, | |||||
'no-useless-constructor': 2, | |||||
'no-useless-escape': 0, | |||||
'no-whitespace-before-property': 2, | |||||
'no-with': 2, | |||||
'one-var': [ | |||||
2, | |||||
{ | |||||
initialized: 'never' | |||||
} | |||||
], | |||||
'operator-linebreak': [ | |||||
2, | |||||
'after', | |||||
{ | |||||
overrides: { | |||||
'?': 'before', | |||||
':': 'before' | |||||
} | |||||
} | |||||
], | |||||
'padded-blocks': [2, 'never'], | |||||
quotes: [ | |||||
2, | |||||
'single', | |||||
{ | |||||
avoidEscape: true, | |||||
allowTemplateLiterals: true | |||||
} | |||||
], | |||||
semi: [2, 'never'], | |||||
'semi-spacing': [ | |||||
2, | |||||
{ | |||||
before: false, | |||||
after: true | |||||
} | |||||
], | |||||
'space-before-blocks': [2, 'always'], | |||||
'space-before-function-paren': 'off', | |||||
'space-in-parens': [2, 'never'], | |||||
'space-infix-ops': 2, | |||||
'space-unary-ops': [ | |||||
2, | |||||
{ | |||||
words: true, | |||||
nonwords: false | |||||
} | |||||
], | |||||
'spaced-comment': [ | |||||
2, | |||||
'always', | |||||
{ | |||||
markers: [ | |||||
'global', | |||||
'globals', | |||||
'eslint', | |||||
'eslint-disable', | |||||
'*package', | |||||
'!', | |||||
',' | |||||
] | |||||
} | |||||
], | |||||
'template-curly-spacing': [2, 'never'], | |||||
'use-isnan': 2, | |||||
'valid-typeof': 2, | |||||
'wrap-iife': [2, 'any'], | |||||
'yield-star-spacing': [2, 'both'], | |||||
yoda: [2, 'never'], | |||||
'prefer-const': 2, | |||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, | |||||
'object-curly-spacing': [ | |||||
2, | |||||
'always', | |||||
{ | |||||
objectsInObjects: true | |||||
} | |||||
], | |||||
'array-bracket-spacing': [2, 'never'] | |||||
} | |||||
} |
@@ -0,0 +1,23 @@ | |||||
.DS_Store | |||||
node_modules/ | |||||
dist/ | |||||
npm-debug.log* | |||||
yarn-debug.log* | |||||
yarn-error.log* | |||||
**/*.log | |||||
tests/**/coverage/ | |||||
tests/e2e/reports | |||||
selenium-debug.log | |||||
# Editor directories and files | |||||
.idea | |||||
*.suo | |||||
*.ntvs* | |||||
*.njsproj | |||||
*.sln | |||||
*.local | |||||
package-lock.json | |||||
vue.config.js | |||||
.devcontainer |
@@ -0,0 +1,5 @@ | |||||
{ | |||||
"singleQuote": true, | |||||
"semi": false, | |||||
"space-before-function-paren": 0 | |||||
} |
@@ -0,0 +1,5 @@ | |||||
language: node_js | |||||
node_js: 10 | |||||
script: npm run test | |||||
notifications: | |||||
email: false |
@@ -0,0 +1,21 @@ | |||||
MIT License | |||||
Copyright (c) 2017-present PanJiaChen | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in all | |||||
copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
SOFTWARE. |
@@ -0,0 +1,222 @@ | |||||
<p align="center"> | |||||
<img width="320" src="https://wpimg.wallstcn.com/ecc53a42-d79b-42e2-8852-5126b810a4c8.svg"> | |||||
</p> | |||||
<p align="center"> | |||||
<a href="https://github.com/vuejs/vue"> | |||||
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue"> | |||||
</a> | |||||
<a href="https://github.com/ElemeFE/element"> | |||||
<img src="https://img.shields.io/badge/element--ui-2.7.0-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="Estado de Construcción"> | |||||
</a> | |||||
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE"> | |||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="Licencia"> | |||||
</a> | |||||
<a href="https://github.com/PanJiaChen/vue-element-admin/releases"> | |||||
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="Liberación Github"> | |||||
</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="Donación"> | |||||
</a> | |||||
</p> | |||||
Español | [English](./README.md) | [简体中文](./README.zh-CN.md) | [日本語](./README.ja.md) | |||||
## Introducción | |||||
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) es una interfáz de administración preparada para producción. Está basada en [vue](https://github.com/vuejs/vue) y usa [element-ui](https://github.com/ElemeFE/element) como conjunto de herramientas de interfáz de usuario. | |||||
Vue Element Admin es una solución práctica basada en la nueva plataforma de desarrollo de vue, construida con soporte a i18 para el manejo de múltiples lenguajes, plantillas estándares para aplicaciones de negocio y un conjunto de asombrosas características. Esta herramienta ayuda a construir largas y complejas Aplicacones de una sola página (SPA). Creo que lo que necesites hacer, este proyecto te ayudará. | |||||
- [Vista Prévia de la Aplicación](https://panjiachen.github.io/vue-element-admin) | |||||
- [Documentación](https://panjiachen.github.io/vue-element-admin-site/) | |||||
- [Canal de Gitter](https://gitter.im/vue-element-admin/discuss) | |||||
- [Para Donaciones](https://panjiachen.github.io/vue-element-admin-site/donate/) | |||||
- [Enlace de Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki) | |||||
- [Canal de Gitee](https://panjiachen.gitee.io/vue-element-admin/) | |||||
- Plantilla base recomendada para usar: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template) | |||||
- Aplicación de Escritorio: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin) | |||||
- Plantilla de Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Créditos: [@Armour](https://github.com/Armour)) | |||||
- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312) | |||||
**Después de la versión `v4.1.0+`, la rama por defecto master no tendrá soporte para i18n. Por favor utilice la rama [i18n](https://github.com/PanJiaChen/vue-element-admin/tree/i18n), los cambios serán incluidos en la rama master** | |||||
**la versión actual es `v4.0+` construida con `vue-cli`. Si encuentra algún problema, por favor coloque un [issue](https://github.com/PanJiaChen/vue-element-admin/issues/new). Si desea usar la versión anterior, puede cambiar de rama a [tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0), no relacionado con `vue-cli`** | |||||
**Este proyecto no está soportado para versiones antigüas de navegadores (ej. IE).** | |||||
## Preparación | |||||
Necesita instalar [node](https://nodejs.org/) y [git](https://git-scm.com/) localmente. El proyecto es basado en [ES2015+](https://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/), [vue-cli](https://github.com/vuejs/vue-cli) , [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), toda la solicitud de datos simulada se realiza a través de [Mock.js](https://github.com/nuysoft/Mock). | |||||
Entendiendo y aprendiendo esto pudiera ayudarle con su proyecto. | |||||
[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/PanJiaChen/vue-element-admin/tree/CodeSandbox) | |||||
<p align="center"> | |||||
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png"> | |||||
</p> | |||||
## Patrocinantes | |||||
Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace directo a su sitio web. [[Se un Patrocinante]](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>Plantilla de Dashboard de administración hecha con Vue, React y Angular.</p> | |||||
## Características | |||||
``` | |||||
- Iniciar / Cerrar Sesión | |||||
- Permisos de Autenticación | |||||
- Página de Permisos | |||||
- Directivas de permisos | |||||
- Página de configuración de permisos | |||||
- Autenticación por dos pasos | |||||
- Construcción Multi-entorno | |||||
- Desarrollo (dev) | |||||
- sit | |||||
- Escenario de pruebas (stage), | |||||
- Producción (prod) | |||||
- Características Globales | |||||
- I18n | |||||
- Temas dinámicos | |||||
- Menu lateral dinámico (soporte a rutas multi-nivel) | |||||
- Barra de rutas dinámica | |||||
- Tags-view (Pestañas de página, Soporta operación de clic derecho) | |||||
- Svg Sprite | |||||
- Datos de simulación con Mock | |||||
- Pantalla completa | |||||
- Menu lateral responsivo | |||||
- Editor | |||||
- Editor de Texto Enriquecido | |||||
- Editor Markdown | |||||
- Editor JSON | |||||
- Excel | |||||
- Exportación a Excel | |||||
- Carga de Excel | |||||
- Visualización de Excel | |||||
- Exportación como ZIP | |||||
- Tabla | |||||
- Tabla Dinámica | |||||
- Tabla con Arrastrar y Soltar | |||||
- Tabla de edición en línea | |||||
- Páginas de Error | |||||
- 401 | |||||
- 404 | |||||
- Componentes | |||||
- Carga de Avatar | |||||
- Botón para subir al inicio | |||||
- Arrastrar y Soltar (Diaglogo) | |||||
- Arrastrar y Soltar (Seleccionar) | |||||
- Arrastrar y Soltar (Kanban) | |||||
- Arrastrar y Soltar (Lista) | |||||
- Panel de división | |||||
- Componente para soltar archivos | |||||
- Adhesión de objetos | |||||
- Contador hasta | |||||
- Ejemplo Avanzado | |||||
- Registro de Errores | |||||
- Tablero de indicadores | |||||
- Página de Guías | |||||
- ECharts (Gráficos) | |||||
- Portapapeles | |||||
- Convertidor de Markdown a HTML | |||||
``` | |||||
## Iniciando | |||||
```bash | |||||
# clone el proyecto | |||||
git clone https://github.com/PanJiaChen/vue-element-admin.git | |||||
# vaya al directorio clonado | |||||
cd vue-element-admin | |||||
# instale las dependencias | |||||
npm install | |||||
# corra el proyecto como desarrollador | |||||
npm run dev | |||||
``` | |||||
Automáticamente se abrirá el siguiente enlace en su navegador http://localhost:9527 | |||||
## Construcción | |||||
```bash | |||||
# Construcción para entornos de prueba | |||||
npm run build:stage | |||||
# Construcción para entornos de producción | |||||
npm run build:prod | |||||
``` | |||||
## Avanzado | |||||
```bash | |||||
# Vista previa con efectos de entorno | |||||
npm run preview | |||||
# Vista previa con efectos + análisis de recursos estáticos | |||||
npm run preview -- --report | |||||
# Chequeo de formato de código | |||||
npm run lint | |||||
# Chequeo de formato de código y auto-corrección | |||||
npm run lint -- --fix | |||||
``` | |||||
Vaya a [Documentación](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) para mayor información | |||||
## Registro de Cambios | |||||
Los cambios detallados por cada liberación se encuentran en [notas de liberación](https://github.com/PanJiaChen/vue-element-admin/releases). | |||||
## Demostración en línea | |||||
[Vista Prévia de la Aplicación](https://panjiachen.github.io/vue-element-admin) | |||||
## Donación | |||||
Si este proyecto es de mucha ayuda para ti, puedes comprarle al autor un vaso de jugo :tropical_drink: | |||||
![Donar](https://wpimg.wallstcn.com/bd273f0d-83a0-4ef2-92e1-9ac8ed3746b9.png) | |||||
[dona por Paypal](https://www.paypal.me/panfree23) | |||||
[Comprame un Café](https://www.buymeacoffee.com/Pan) | |||||
## Navegadores Soportados | |||||
Navegadores modernos e 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" />](https://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" />](https://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" />](https://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" />](https://godban.github.io/browsers-support-badges/)</br>Safari | | |||||
| --------- | --------- | --------- | --------- | | |||||
| IE10, IE11, Edge | últimas 2 versiones | últimas 2 versiones | últimas 2 versiones | | |||||
## Licencia | |||||
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE) | |||||
Copyright (c) 2017-presente PanJiaChen |
@@ -0,0 +1,10 @@ | |||||
# 开发规范文档 | |||||
- 组件命名与配置 | |||||
1. 使用大驼峰命名组件 vue 文件,导出组件命名与文件名相同. | |||||
2. 组件样式一定是要 scoped 的,以免样式冲突. | |||||
3. 前端开发工具统一使用 vscode,统一使用格式化插件配置(前端群文件中). | |||||
4. 组件初始模板的生成,使用统一的代码片段进行生成,不要去更改内部属性的排列顺序,删掉属性是可以的. | |||||
- 组件编写与引用 | |||||
1. 通用组件标签部分,最外层标签为组件盒子,它用于隔离内在样式与外在样式的干扰. | |||||
2. |
@@ -0,0 +1,245 @@ | |||||
<p align="center"> | |||||
<img width="320" src="https://wpimg.wallstcn.com/ecc53a42-d79b-42e2-8852-5126b810a4c8.svg"> | |||||
</p> | |||||
出现nodejieba安装错误的信息,请执行 npm install --global --production windows-build-tools ,不动的情况下就回车一下试试看. | |||||
<p align="center"> | |||||
<a href="https://github.com/vuejs/vue"> | |||||
<img src="https://img.shields.io/badge/vue-2.6.10-brightgreen.svg" alt="vue"> | |||||
</a> | |||||
<a href="https://github.com/ElemeFE/element"> | |||||
<img src="https://img.shields.io/badge/element--ui-2.7.0-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"> | |||||
</a> | |||||
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE"> | |||||
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license"> | |||||
</a> | |||||
<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) | [日本語](./README.ja.md) | [Spanish](./README.es.md) | |||||
## 简介 | |||||
[vue-element-admin](https://panjiachen.github.io/vue-element-admin) 是一个后台前端解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element-ui](https://github.com/ElemeFE/element)实现。它使用了最新的前端技术栈,内置了 i18n 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。 | |||||
- [在线预览](https://panjiachen.github.io/vue-element-admin) | |||||
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/) | |||||
- [Gitter 讨论组](https://gitter.im/vue-element-admin/discuss) | |||||
- [Donate](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate) | |||||
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki) | |||||
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 在线预览(国内用户可访问该地址) | |||||
- [国内访问文档](https://panjiachen.gitee.io/vue-element-admin-site/zh/) 文档(方便没翻墙的用户查看) | |||||
- 基础模板建议使用: [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)) | |||||
- [awesome-project](https://github.com/PanJiaChen/vue-element-admin/issues/2312) | |||||
**`v4.1.0+`版本之后默认 master 分支将不支持国际化,有需要的请使用[i18n](https://github.com/PanJiaChen/vue-element-admin/tree/i18n)分支,它会和 master 保持同步更新** | |||||
**该项目不支持低版本浏览器(如 ie),有需求请自行添加 polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)** | |||||
**目前版本为 `v4.0+` 基于 `vue-cli` 进行构建,若发现问题,欢迎提[issue](https://github.com/PanJiaChen/vue-element-admin/issues/new)。若你想使用旧版本,可以切换分支到[tag/3.11.0](https://github.com/PanJiaChen/vue-element-admin/tree/tag/3.11.0),它不依赖 `vue-cli`** | |||||
群主 **[圈子](https://jianshiapp.com/circles/1209)** 群主会经常分享一些技术相关的东西,或者加入 [qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602) 或者关注 [微博](https://weibo.com/u/3423485724?is_all=1) | |||||
## 前序准备 | |||||
你需要在本地安装 [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/) 、[vue-cli](https://github.com/vuejs/vue-cli) 、[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撸后台 系列五(v4.0新版本)](https://juejin.im/post/5c92ff94f265da6128275a85) | |||||
- [手摸手,带你封装一个 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** | |||||
[![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/PanJiaChen/vue-element-admin/tree/CodeSandbox) | |||||
<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> | |||||
## 功能 | |||||
``` | |||||
- 登录 / 注销 | |||||
- 权限验证 | |||||
- 页面权限 | |||||
- 指令权限 | |||||
- 权限配置 | |||||
- 二步登录 | |||||
- 多环境发布 | |||||
- dev | |||||
- sit | |||||
- stage | |||||
- prod | |||||
- 全局功能 | |||||
- 国际化多语言 | |||||
- 多种动态换肤 | |||||
- 动态侧边栏(支持多级路由嵌套) | |||||
- 动态面包屑 | |||||
- 快捷导航(标签页) | |||||
- Svg Sprite 图标 | |||||
- 本地/后端 mock 数据 | |||||
- Screenfull全屏 | |||||
- 自适应收缩侧边栏 | |||||
- 编辑器 | |||||
- 富文本 | |||||
- Markdown | |||||
- JSON 等多格式 | |||||
- Excel | |||||
- 导出excel | |||||
- 导入excel | |||||
- 前端可视化excel | |||||
- 导出zip | |||||
- 表格 | |||||
- 动态表格 | |||||
- 拖拽表格 | |||||
- 内联编辑 | |||||
- 错误页面 | |||||
- 401 | |||||
- 404 | |||||
- 組件 | |||||
- 头像上传 | |||||
- 返回顶部 | |||||
- 拖拽Dialog | |||||
- 拖拽Select | |||||
- 拖拽看板 | |||||
- 列表拖拽 | |||||
- SplitPane | |||||
- Dropzone | |||||
- Sticky | |||||
- CountTo | |||||
- 综合实例 | |||||
- 错误日志 | |||||
- Dashboard | |||||
- 引导页 | |||||
- ECharts 图表 | |||||
- Clipboard(剪贴复制) | |||||
- Markdown2html | |||||
``` | |||||
## 开发 | |||||
```bash | |||||
# 克隆项目 | |||||
git clone -b i18n git@github.com:PanJiaChen/vue-element-admin.git | |||||
# 进入项目目录 | |||||
cd vue-element-admin | |||||
# 安装依赖 | |||||
npm install | |||||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 | |||||
npm install --registry=https://registry.npm.taobao.org | |||||
# 启动服务 | |||||
npm run dev | |||||
``` | |||||
浏览器访问 http://localhost:9527 | |||||
## 发布 | |||||
```bash | |||||
# 构建测试环境 | |||||
npm run build:stage | |||||
# 构建生产环境 | |||||
npm run build:prod | |||||
``` | |||||
## 其它 | |||||
```bash | |||||
# 预览发布环境效果 | |||||
npm run preview | |||||
# 预览发布环境效果 + 静态资源分析 | |||||
npm run preview -- --report | |||||
# 代码格式检查 | |||||
npm run lint | |||||
# 代码格式检查并自动修复 | |||||
npm run lint -- --fix | |||||
``` | |||||
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/) | |||||
## Changelog | |||||
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases). | |||||
## Online Demo | |||||
[在线 Demo](https://panjiachen.github.io/vue-element-admin) | |||||
## Donate | |||||
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink: | |||||
![donate](https://panjiachen.github.io/donate/donation.png) | |||||
[更多捐赠方式](https://panjiachen.gitee.io/vue-element-admin-site/zh/donate) | |||||
[Paypal Me](https://www.paypal.me/panfree23) | |||||
[Buy me a coffee](https://www.buymeacoffee.com/Pan) | |||||
## 购买贴纸 | |||||
你也可以通过 购买[官方授权的贴纸](https://smallsticker.com/product/vue-element-admin) 的方式来支持 vue-element-admin - 每售出一张贴纸,本项目将获得 2 元的捐赠。 | |||||
## 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" />](https://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" />](https://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" />](https://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" />](https://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) | |||||
Copyright (c) 2017-present PanJiaChen |
@@ -0,0 +1,14 @@ | |||||
module.exports = { | |||||
presets: [ | |||||
// https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app | |||||
'@vue/cli-plugin-babel/preset' | |||||
], | |||||
'env': { | |||||
'development': { | |||||
// babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). | |||||
// This plugin can significantly increase the speed of hot updates, when you have a large number of pages. | |||||
// https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html | |||||
'plugins': ['dynamic-import-node'] | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
module.exports = { | |||||
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'], | |||||
transform: { | |||||
'^.+\\.vue$': 'vue-jest', | |||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': | |||||
'jest-transform-stub', | |||||
'^.+\\.jsx?$': 'babel-jest' | |||||
}, | |||||
moduleNameMapper: { | |||||
'^@/(.*)$': '<rootDir>/src/$1' | |||||
}, | |||||
snapshotSerializers: ['jest-serializer-vue'], | |||||
testMatch: [ | |||||
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' | |||||
], | |||||
collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'], | |||||
coverageDirectory: '<rootDir>/tests/unit/coverage', | |||||
// 'collectCoverage': true, | |||||
'coverageReporters': [ | |||||
'lcov', | |||||
'text-summary' | |||||
], | |||||
testURL: 'http://localhost/' | |||||
} |
@@ -0,0 +1,9 @@ | |||||
{ | |||||
"compilerOptions": { | |||||
"baseUrl": "./", | |||||
"paths": { | |||||
"@/*": ["src/*"] | |||||
} | |||||
}, | |||||
"exclude": ["node_modules", "dist"] | |||||
} |
@@ -0,0 +1,116 @@ | |||||
const Mock = require('mockjs') | |||||
const List = [] | |||||
const count = 100 | |||||
const baseContent = '<p>I am testing data, I am testing data.</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'], | |||||
display_time: '@datetime', | |||||
comment_disabled: true, | |||||
pageviews: '@integer(300, 5000)', | |||||
image_uri, | |||||
platforms: ['a-platform'] | |||||
})) | |||||
} | |||||
module.exports = [ | |||||
{ | |||||
url: '/vue-element-admin/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: '/vue-element-admin/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: '/vue-element-admin/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: '/vue-element-admin/article/create', | |||||
type: 'post', | |||||
response: _ => { | |||||
return { | |||||
code: 20000, | |||||
data: 'success' | |||||
} | |||||
} | |||||
}, | |||||
{ | |||||
url: '/vue-element-admin/article/update', | |||||
type: 'post', | |||||
response: _ => { | |||||
return { | |||||
code: 20000, | |||||
data: 'success' | |||||
} | |||||
} | |||||
} | |||||
] | |||||
@@ -0,0 +1,60 @@ | |||||
const Mock = require('mockjs') | |||||
const { param2Obj } = require('./utils') | |||||
const user = require('./user') | |||||
const role = require('./role') | |||||
const article = require('./article') | |||||
const search = require('./remote-search') | |||||
const mocks = [ | |||||
...user, | |||||
...role, | |||||
...article, | |||||
...search | |||||
] | |||||
// for front mock | |||||
// please use it cautiously, it will redefine XMLHttpRequest, | |||||
// which will cause many of your third-party libraries to be invalidated(like progress event). | |||||
function mockXHR() { | |||||
// mock patch | |||||
// https://github.com/nuysoft/Mock/issues/300 | |||||
Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send | |||||
Mock.XHR.prototype.send = function() { | |||||
if (this.custom.xhr) { | |||||
this.custom.xhr.withCredentials = this.withCredentials || false | |||||
if (this.responseType) { | |||||
this.custom.xhr.responseType = this.responseType | |||||
} | |||||
} | |||||
this.proxy_send(...arguments) | |||||
} | |||||
function XHR2ExpressReqWrap(respond) { | |||||
return function(options) { | |||||
let result = null | |||||
if (respond instanceof Function) { | |||||
const { body, type, url } = options | |||||
// https://expressjs.com/en/4x/api.html#req | |||||
result = respond({ | |||||
method: type, | |||||
body: JSON.parse(body), | |||||
query: param2Obj(url) | |||||
}) | |||||
} else { | |||||
result = respond | |||||
} | |||||
return Mock.mock(result) | |||||
} | |||||
} | |||||
for (const i of mocks) { | |||||
Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response)) | |||||
} | |||||
} | |||||
module.exports = { | |||||
mocks, | |||||
mockXHR | |||||
} |
@@ -0,0 +1,81 @@ | |||||
const chokidar = require('chokidar') | |||||
const bodyParser = require('body-parser') | |||||
const chalk = require('chalk') | |||||
const path = require('path') | |||||
const Mock = require('mockjs') | |||||
const mockDir = path.join(process.cwd(), 'mock') | |||||
function registerRoutes(app) { | |||||
let mockLastIndex | |||||
const { mocks } = require('./index.js') | |||||
const mocksForServer = mocks.map(route => { | |||||
return responseFake(route.url, route.type, route.response) | |||||
}) | |||||
for (const mock of mocksForServer) { | |||||
app[mock.type](mock.url, mock.response) | |||||
mockLastIndex = app._router.stack.length | |||||
} | |||||
const mockRoutesLength = Object.keys(mocksForServer).length | |||||
return { | |||||
mockRoutesLength: mockRoutesLength, | |||||
mockStartIndex: mockLastIndex - mockRoutesLength | |||||
} | |||||
} | |||||
function unregisterRoutes() { | |||||
Object.keys(require.cache).forEach(i => { | |||||
if (i.includes(mockDir)) { | |||||
delete require.cache[require.resolve(i)] | |||||
} | |||||
}) | |||||
} | |||||
// for mock server | |||||
const responseFake = (url, type, respond) => { | |||||
return { | |||||
url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`), | |||||
type: type || 'get', | |||||
response(req, res) { | |||||
console.log('request invoke:' + req.path) | |||||
res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond)) | |||||
} | |||||
} | |||||
} | |||||
module.exports = app => { | |||||
// parse app.body | |||||
// https://expressjs.com/en/4x/api.html#req.body | |||||
app.use(bodyParser.json()) | |||||
app.use(bodyParser.urlencoded({ | |||||
extended: true | |||||
})) | |||||
const mockRoutes = registerRoutes(app) | |||||
var mockRoutesLength = mockRoutes.mockRoutesLength | |||||
var mockStartIndex = mockRoutes.mockStartIndex | |||||
// watch files, hot reload mock server | |||||
chokidar.watch(mockDir, { | |||||
ignored: /mock-server/, | |||||
ignoreInitial: true | |||||
}).on('all', (event, path) => { | |||||
if (event === 'change' || event === 'add') { | |||||
try { | |||||
// remove mock routes stack | |||||
app._router.stack.splice(mockStartIndex, mockRoutesLength) | |||||
// clear routes cache | |||||
unregisterRoutes() | |||||
const mockRoutes = registerRoutes(app) | |||||
mockRoutesLength = mockRoutes.mockRoutesLength | |||||
mockStartIndex = mockRoutes.mockStartIndex | |||||
console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed ${path}`)) | |||||
} catch (error) { | |||||
console.log(chalk.redBright(error)) | |||||
} | |||||
} | |||||
}) | |||||
} |
@@ -0,0 +1,51 @@ | |||||
const Mock = require('mockjs') | |||||
const NameList = [] | |||||
const count = 100 | |||||
for (let i = 0; i < count; i++) { | |||||
NameList.push(Mock.mock({ | |||||
name: '@first' | |||||
})) | |||||
} | |||||
NameList.push({ name: 'mock-Pan' }) | |||||
module.exports = [ | |||||
// username search | |||||
{ | |||||
url: '/vue-element-admin/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: '/vue-element-admin/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'] | |||||
}] | |||||
} | |||||
} | |||||
} | |||||
} | |||||
] |
@@ -0,0 +1,98 @@ | |||||
const Mock = require('mockjs') | |||||
const { deepClone } = require('../utils') | |||||
const { asyncRoutes, constantRoutes } = require('./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' } | |||||
} | |||||
] | |||||
}] | |||||
} | |||||
] | |||||
module.exports = [ | |||||
// mock get all routes form server | |||||
{ | |||||
url: '/vue-element-admin/routes', | |||||
type: 'get', | |||||
response: _ => { | |||||
return { | |||||
code: 20000, | |||||
data: routes | |||||
} | |||||
} | |||||
}, | |||||
// mock get all roles form server | |||||
{ | |||||
url: '/vue-element-admin/roles', | |||||
type: 'get', | |||||
response: _ => { | |||||
return { | |||||
code: 20000, | |||||
data: roles | |||||
} | |||||
} | |||||
}, | |||||
// add role | |||||
{ | |||||
url: '/vue-element-admin/role', | |||||
type: 'post', | |||||
response: { | |||||
code: 20000, | |||||
data: { | |||||
key: Mock.mock('@integer(300, 5000)') | |||||
} | |||||
} | |||||
}, | |||||
// update role | |||||
{ | |||||
url: '/vue-element-admin/role/[A-Za-z0-9]', | |||||
type: 'put', | |||||
response: { | |||||
code: 20000, | |||||
data: { | |||||
status: 'success' | |||||
} | |||||
} | |||||
}, | |||||
// delete role | |||||
{ | |||||
url: '/vue-element-admin/role/[A-Za-z0-9]', | |||||
type: 'delete', | |||||
response: { | |||||
code: 20000, | |||||
data: { | |||||
status: 'success' | |||||
} | |||||
} | |||||
} | |||||
] |
@@ -0,0 +1,530 @@ | |||||
// Just a mock data | |||||
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/auth-redirect', | |||||
hidden: true | |||||
}, | |||||
{ | |||||
path: '/404', | |||||
component: 'views/error-page/404', | |||||
hidden: true | |||||
}, | |||||
{ | |||||
path: '/401', | |||||
component: 'views/error-page/401', | |||||
hidden: true | |||||
}, | |||||
{ | |||||
path: '', | |||||
component: 'layout/Layout', | |||||
redirect: 'dashboard', | |||||
children: [ | |||||
{ | |||||
path: 'dashboard', | |||||
component: 'views/dashboard/index', | |||||
name: 'Dashboard', | |||||
meta: { title: 'dashboard', icon: 'dashboard', 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 } | |||||
} | |||||
] | |||||
} | |||||
] | |||||
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/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/json-editor', | |||||
name: 'JsonEditorDemo', | |||||
meta: { title: 'jsonEditor' } | |||||
}, | |||||
{ | |||||
path: 'split-pane', | |||||
component: 'views/components-demo/split-pane', | |||||
name: 'SplitpaneDemo', | |||||
meta: { title: 'splitPane' } | |||||
}, | |||||
{ | |||||
path: 'avatar-upload', | |||||
component: 'views/components-demo/avatar-upload', | |||||
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/count-to', | |||||
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/back-to-top', | |||||
name: 'BackToTopDemo', | |||||
meta: { title: 'backToTop' } | |||||
}, | |||||
{ | |||||
path: 'drag-dialog', | |||||
component: 'views/components-demo/drag-dialog', | |||||
name: 'DragDialogDemo', | |||||
meta: { title: 'dragDialog' } | |||||
}, | |||||
{ | |||||
path: 'drag-select', | |||||
component: 'views/components-demo/drag-select', | |||||
name: 'DragSelectDemo', | |||||
meta: { title: 'dragSelect' } | |||||
}, | |||||
{ | |||||
path: 'dnd-list', | |||||
component: 'views/components-demo/dnd-list', | |||||
name: 'DndListDemo', | |||||
meta: { title: 'dndList' } | |||||
}, | |||||
{ | |||||
path: 'drag-kanban', | |||||
component: 'views/components-demo/drag-kanban', | |||||
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/error-page/401', | |||||
name: 'Page401', | |||||
meta: { title: 'page401', noCache: true } | |||||
}, | |||||
{ | |||||
path: '404', | |||||
component: 'views/error-page/404', | |||||
name: 'Page404', | |||||
meta: { title: 'page404', noCache: true } | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
path: '/error-log', | |||||
component: 'layout/Layout', | |||||
redirect: 'noRedirect', | |||||
children: [ | |||||
{ | |||||
path: 'log', | |||||
component: 'views/error-log/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/export-excel', | |||||
name: 'ExportExcel', | |||||
meta: { title: 'exportExcel' } | |||||
}, | |||||
{ | |||||
path: 'export-selected-excel', | |||||
component: 'views/excel/select-excel', | |||||
name: 'SelectExcel', | |||||
meta: { title: 'selectExcel' } | |||||
}, | |||||
{ | |||||
path: 'export-merge-header', | |||||
component: 'views/excel/merge-header', | |||||
name: 'MergeHeader', | |||||
meta: { title: 'mergeHeader' } | |||||
}, | |||||
{ | |||||
path: 'upload-excel', | |||||
component: 'views/excel/upload-excel', | |||||
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 } | |||||
] | |||||
module.exports = { | |||||
constantRoutes, | |||||
asyncRoutes | |||||
} |
@@ -0,0 +1,85 @@ | |||||
const tokens = { | |||||
'19923693200': { | |||||
token: 'admin-token' | |||||
}, | |||||
editor: { | |||||
token: 'editor-token' | |||||
} | |||||
} | |||||
const users = { | |||||
'admin-token': { | |||||
roles: ['admin'], | |||||
introduction: 'I am a super administrator', | |||||
avatar: | |||||
'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', | |||||
name: 'Super Admin' | |||||
}, | |||||
'editor-token': { | |||||
roles: ['editor'], | |||||
introduction: 'I am an editor', | |||||
avatar: | |||||
'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif', | |||||
name: 'Normal Editor' | |||||
} | |||||
} | |||||
module.exports = [ | |||||
// user login | |||||
{ | |||||
url: '/vue-element-admin/user/login', | |||||
type: 'post', | |||||
response: config => { | |||||
const { phone } = config.body | |||||
const token = tokens[phone] | |||||
// mock error | |||||
if (!token) { | |||||
return { | |||||
code: 60204, | |||||
message: 'Account and password are incorrect.' | |||||
} | |||||
} | |||||
return { | |||||
code: 20000, | |||||
data: token | |||||
} | |||||
} | |||||
}, | |||||
// get user info | |||||
{ | |||||
url: '/vue-element-admin/user/info.*', | |||||
type: 'get', | |||||
response: config => { | |||||
const { token } = config.query | |||||
const info = users[token] | |||||
// mock error | |||||
if (!info) { | |||||
return { | |||||
code: 50008, | |||||
message: 'Login failed, unable to get user details.' | |||||
} | |||||
} | |||||
return { | |||||
code: 20000, | |||||
data: info | |||||
} | |||||
} | |||||
}, | |||||
// user logout | |||||
{ | |||||
url: '/vue-element-admin/user/logout', | |||||
type: 'post', | |||||
response: _ => { | |||||
return { | |||||
code: 20000, | |||||
data: 'success' | |||||
} | |||||
} | |||||
} | |||||
] |
@@ -0,0 +1,48 @@ | |||||
/** | |||||
* @param {string} url | |||||
* @returns {Object} | |||||
*/ | |||||
function param2Obj(url) { | |||||
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') | |||||
if (!search) { | |||||
return {} | |||||
} | |||||
const obj = {} | |||||
const searchArr = search.split('&') | |||||
searchArr.forEach(v => { | |||||
const index = v.indexOf('=') | |||||
if (index !== -1) { | |||||
const name = v.substring(0, index) | |||||
const val = v.substring(index + 1, v.length) | |||||
obj[name] = val | |||||
} | |||||
}) | |||||
return obj | |||||
} | |||||
/** | |||||
* This is just a simple version of deep copy | |||||
* Has a lot of edge cases bug | |||||
* If you want to use a perfect deep copy, use lodash's _.cloneDeep | |||||
* @param {Object} source | |||||
* @returns {Object} | |||||
*/ | |||||
function deepClone(source) { | |||||
if (!source && typeof source !== 'object') { | |||||
throw new Error('error arguments', 'deepClone') | |||||
} | |||||
const targetObj = source.constructor === Array ? [] : {} | |||||
Object.keys(source).forEach(keys => { | |||||
if (source[keys] && typeof source[keys] === 'object') { | |||||
targetObj[keys] = deepClone(source[keys]) | |||||
} else { | |||||
targetObj[keys] = source[keys] | |||||
} | |||||
}) | |||||
return targetObj | |||||
} | |||||
module.exports = { | |||||
param2Obj, | |||||
deepClone | |||||
} |
@@ -0,0 +1,121 @@ | |||||
{ | |||||
"name": "funoos", | |||||
"version": "4.3.1", | |||||
"description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features", | |||||
"author": "Pan <panfree23@gmail.com>", | |||||
"scripts": { | |||||
"dev": "vue-cli-service serve", | |||||
"lint": "eslint --ext .js,.vue src", | |||||
"build:prod": "vue-cli-service build", | |||||
"build:stage": "vue-cli-service build --mode staging", | |||||
"preview": "node build/index.js --preview", | |||||
"new": "plop", | |||||
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", | |||||
"test:unit": "jest --clearCache && vue-cli-service test:unit", | |||||
"test:ci": "npm run lint && npm run test:unit" | |||||
}, | |||||
"dependencies": { | |||||
"axios": "^0.21.1", | |||||
"bignumber.js": "^9.0.1", | |||||
"clipboard": "^2.0.4", | |||||
"codemirror": "5.45.0", | |||||
"core-js": "3.6.5", | |||||
"crypto-js": "^4.0.0", | |||||
"driver.js": "0.9.5", | |||||
"dropzone": "5.5.1", | |||||
"echarts": "4.2.1", | |||||
"element-ui": "2.13.2", | |||||
"file-saver": "2.0.1", | |||||
"fuse.js": "3.4.4", | |||||
"hevue-img-preview": "^3.0.0", | |||||
"js-cookie": "2.2.0", | |||||
"jsonlint": "1.6.3", | |||||
"jszip": "3.2.1", | |||||
"lodash": "^4.17.20", | |||||
"normalize.css": "7.0.0", | |||||
"nprogress": "0.2.0", | |||||
"path-to-regexp": "2.4.0", | |||||
"pinyin": "2.9.0", | |||||
"screenfull": "4.2.0", | |||||
"script-loader": "0.7.2", | |||||
"sortablejs": "1.8.4", | |||||
"tui-editor": "1.3.3", | |||||
"vue": "2.6.10", | |||||
"vue-count-to": "1.0.13", | |||||
"vue-i18n": "^7.3.2", | |||||
"vue-mini-player": "^0.2.1", | |||||
"vue-router": "3.0.2", | |||||
"vue-splitpane": "1.0.4", | |||||
"vuedraggable": "2.20.0", | |||||
"vuex": "3.1.0", | |||||
"xlsx": "0.14.1" | |||||
}, | |||||
"devDependencies": { | |||||
"@vue/cli-plugin-babel": "4.4.4", | |||||
"@vue/cli-plugin-eslint": "4.4.4", | |||||
"@vue/cli-plugin-unit-jest": "^4.5.9", | |||||
"@vue/cli-service": "4.4.4", | |||||
"@vue/test-utils": "1.0.0-beta.29", | |||||
"autoprefixer": "9.5.1", | |||||
"babel-eslint": "10.1.0", | |||||
"babel-jest": "^26.6.3", | |||||
"babel-plugin-dynamic-import-node": "2.3.3", | |||||
"chalk": "2.4.2", | |||||
"chokidar": "2.1.5", | |||||
"connect": "3.6.6", | |||||
"eslint": "6.7.2", | |||||
"eslint-plugin-vue": "6.2.2", | |||||
"html-webpack-plugin": "3.2.0", | |||||
"husky": "1.3.1", | |||||
"less": "^4.1.1", | |||||
"less-loader": "^7.3.0", | |||||
"lint-staged": "8.1.5", | |||||
"mockjs": "1.0.1-beta3", | |||||
"plop": "2.3.0", | |||||
"runjs": "^4.4.2", | |||||
"sass": "1.26.2", | |||||
"sass-loader": "8.0.2", | |||||
"sass-resources-loader": "^2.1.0", | |||||
"script-ext-html-webpack-plugin": "2.1.3", | |||||
"serve-static": "1.13.2", | |||||
"svg-sprite-loader": "4.1.3", | |||||
"svgo": "1.2.0", | |||||
"vue-template-compiler": "2.6.10" | |||||
}, | |||||
"browserslist": [ | |||||
"> 1%", | |||||
"last 2 versions" | |||||
], | |||||
"bugs": { | |||||
"url": "https://github.com/PanJiaChen/vue-element-admin/issues" | |||||
}, | |||||
"engines": { | |||||
"node": ">=8.9", | |||||
"npm": ">= 3.0.0" | |||||
}, | |||||
"keywords": [ | |||||
"vue", | |||||
"admin", | |||||
"dashboard", | |||||
"element-ui", | |||||
"boilerplate", | |||||
"admin-template", | |||||
"management-system" | |||||
], | |||||
"license": "MIT", | |||||
"lint-staged": { | |||||
"src/**/*.{js,vue}": [ | |||||
"eslint --fix", | |||||
"git add" | |||||
] | |||||
}, | |||||
"husky": { | |||||
"hooks": { | |||||
"pre-commit": "lint-staged" | |||||
} | |||||
}, | |||||
"repository": { | |||||
"type": "git", | |||||
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git" | |||||
} | |||||
} |
@@ -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}} |
@@ -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 | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
{{#if state}} | |||||
const state = {} | |||||
{{/if}} | |||||
{{#if mutations}} | |||||
const mutations = {} | |||||
{{/if}} | |||||
{{#if actions}} | |||||
const actions = {} | |||||
{{/if}} | |||||
export default { | |||||
namespaced: true, | |||||
{{options}} | |||||
} |
@@ -0,0 +1,62 @@ | |||||
const { notEmpty } = require('../utils.js') | |||||
module.exports = { | |||||
description: 'generate store', | |||||
prompts: [{ | |||||
type: 'input', | |||||
name: 'name', | |||||
message: 'store name please', | |||||
validate: notEmpty('name') | |||||
}, | |||||
{ | |||||
type: 'checkbox', | |||||
name: 'blocks', | |||||
message: 'Blocks:', | |||||
choices: [{ | |||||
name: 'state', | |||||
value: 'state', | |||||
checked: true | |||||
}, | |||||
{ | |||||
name: 'mutations', | |||||
value: 'mutations', | |||||
checked: true | |||||
}, | |||||
{ | |||||
name: 'actions', | |||||
value: 'actions', | |||||
checked: true | |||||
} | |||||
], | |||||
validate(value) { | |||||
if (!value.includes('state') || !value.includes('mutations')) { | |||||
return 'store require at least state and mutations' | |||||
} | |||||
return true | |||||
} | |||||
} | |||||
], | |||||
actions(data) { | |||||
const name = '{{name}}' | |||||
const { blocks } = data | |||||
const options = ['state', 'mutations'] | |||||
const joinFlag = `, | |||||
` | |||||
if (blocks.length === 3) { | |||||
options.push('actions') | |||||
} | |||||
const actions = [{ | |||||
type: 'add', | |||||
path: `src/store/modules/${name}.js`, | |||||
templateFile: 'plop-templates/store/index.hbs', | |||||
data: { | |||||
options: options.join(joinFlag), | |||||
state: blocks.includes('state'), | |||||
mutations: blocks.includes('mutations'), | |||||
actions: blocks.includes('actions') | |||||
} | |||||
}] | |||||
return actions | |||||
} | |||||
} |
@@ -0,0 +1,2 @@ | |||||
exports.notEmpty = name => v => | |||||
!v || v.trim() === '' ? `${name} is required` : true |
@@ -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}} |
@@ -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 | |||||
} | |||||
} |
@@ -0,0 +1,9 @@ | |||||
const viewGenerator = require('./plop-templates/view/prompt') | |||||
const componentGenerator = require('./plop-templates/component/prompt') | |||||
const storeGenerator = require('./plop-templates/store/prompt.js') | |||||
module.exports = function(plop) { | |||||
plop.setGenerator('view', viewGenerator) | |||||
plop.setGenerator('component', componentGenerator) | |||||
plop.setGenerator('store', storeGenerator) | |||||
} |
@@ -0,0 +1,5 @@ | |||||
module.exports = { | |||||
plugins: { | |||||
autoprefixer: {} | |||||
} | |||||
} |
@@ -0,0 +1,15 @@ | |||||
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<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"> | |||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> | |||||
<title><%= webpackConfig.name %></title> | |||||
</head> | |||||
<body> | |||||
<div id="app"></div> | |||||
<!-- built files will be auto injected --> | |||||
</body> | |||||
</html> |