Compare commits
No commits in common. "vue3.0-antdv" and "master" have entirely different histories.
vue3.0-ant
...
master
|
|
@ -1,3 +1,5 @@
|
|||
# 支持浏览器配置
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# 编辑器配置
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
src/assets
|
||||
src/icons
|
||||
public
|
||||
dist
|
||||
node_modules
|
||||
vab-icon
|
||||
layouts
|
||||
119
.eslintrc.js
|
|
@ -1,14 +1,123 @@
|
|||
/**
|
||||
* @author https://vue-admin-beautiful.com (不想保留author可删除)
|
||||
* @description .eslintrc.js
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/prettier'],
|
||||
extends: ['plugin:vue/recommended', 'eslint:recommended', '@vue/prettier'],
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
'no-console': 'off',
|
||||
'no-debugger': 'off',
|
||||
'prettier/prettier': 'warn',
|
||||
'prefer-template': 'error',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
'vue/no-v-html': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'vue/no-useless-template-attributes': 'off',
|
||||
'use-isnan': 'off',
|
||||
'vue/html-self-closing': [
|
||||
'error',
|
||||
{
|
||||
html: {
|
||||
void: 'any',
|
||||
normal: 'any',
|
||||
component: 'always',
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always',
|
||||
},
|
||||
],
|
||||
'vue/component-name-in-template-casing': [
|
||||
'error',
|
||||
'kebab-case',
|
||||
{
|
||||
registeredComponentsOnly: false,
|
||||
ignores: [],
|
||||
},
|
||||
],
|
||||
// 多字组件名称
|
||||
'vue/multi-word-component-names': 'off',
|
||||
// Vue组件排序
|
||||
'vue/order-in-components': [
|
||||
'warn',
|
||||
{
|
||||
order: [
|
||||
'el',
|
||||
'name',
|
||||
'key',
|
||||
'parent',
|
||||
'functional',
|
||||
['delimiters', 'comments'],
|
||||
['components', 'directives', 'filters'],
|
||||
'extends',
|
||||
'mixins',
|
||||
['provide', 'inject'],
|
||||
'ROUTER_GUARDS',
|
||||
'layout',
|
||||
'middleware',
|
||||
'validate',
|
||||
'scrollToTop',
|
||||
'transition',
|
||||
'loading',
|
||||
'inheritAttrs',
|
||||
'model',
|
||||
['props', 'propsData'],
|
||||
'emits',
|
||||
'setup',
|
||||
'fetch',
|
||||
'asyncData',
|
||||
'data',
|
||||
'head',
|
||||
'computed',
|
||||
'watch',
|
||||
'watchQuery',
|
||||
'LIFECYCLE_HOOKS',
|
||||
'methods',
|
||||
['template', 'render'],
|
||||
'renderError',
|
||||
],
|
||||
},
|
||||
],
|
||||
// Vue属性排序
|
||||
'vue/attributes-order': [
|
||||
'warn',
|
||||
{
|
||||
order: [
|
||||
'DEFINITION',
|
||||
'LIST_RENDERING',
|
||||
'CONDITIONALS',
|
||||
'RENDER_MODIFIERS',
|
||||
'GLOBAL',
|
||||
'UNIQUE',
|
||||
'TWO_WAY_BINDING',
|
||||
'OTHER_DIRECTIVES',
|
||||
'OTHER_ATTR',
|
||||
'EVENTS',
|
||||
'CONTENT',
|
||||
],
|
||||
alphabetical: true, //字母顺序
|
||||
},
|
||||
],
|
||||
},
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint',
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)'],
|
||||
env: {
|
||||
jest: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# .gitattributes
|
||||
*.html text eol=lf
|
||||
*.css text eol=lf
|
||||
*.js text eol=lf linguist-language=vue
|
||||
*.js text eol=lf
|
||||
*.scss text eol=lf
|
||||
*.vue text eol=lf
|
||||
*.hbs text eol=lf
|
||||
|
|
@ -8,3 +9,6 @@
|
|||
*.md text eol=lf
|
||||
*.json text eol=lf
|
||||
*.yml text eol=lf
|
||||
.browserslistrc text eol=lf
|
||||
.gitignore text eol=lf
|
||||
*.js linguist-language=vue
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: chuzhixin
|
||||
#open_collective: vue-admin-plus
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://opencollective.com/vue-admin-plus # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
|
|
@ -1,27 +1,26 @@
|
|||
# .gitignore
|
||||
.DS_Store
|
||||
node_modules
|
||||
dist
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
*.zip
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn.lock
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
|
||||
public/video
|
||||
*.zip
|
||||
*.7z
|
||||
/src/layouts/components/layouts
|
||||
/zx-templates
|
||||
/package-lock.json
|
||||
/src/styles/themes/green.scss
|
||||
/src/styles/themes/dark.scss
|
||||
/src/styles/themes/glory.scss
|
||||
/.history
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* @author https://vue-admin-beautiful.com (不想保留author可删除)
|
||||
* @description stylelint
|
||||
*/
|
||||
module.exports = {
|
||||
extends: ['stylelint-config-recess-order', 'stylelint-config-prettier'],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true
|
||||
},
|
||||
"workbench.colorTheme": "One Monokai",
|
||||
"editor.tabSize": 2,
|
||||
"editor.detectIndentation": false,
|
||||
"emmet.triggerExpansionOnTab": true,
|
||||
"editor.formatOnSave": true,
|
||||
"javascript.format.enable": true,
|
||||
"git.enableSmartCommit": true,
|
||||
"git.autofetch": true,
|
||||
"git.confirmSync": false,
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"liveServer.settings.donotShowInfoMsg": true,
|
||||
"explorer.confirmDelete": false,
|
||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||
"files.exclude": {
|
||||
"**/.idea": true
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.stylelint": "explicit",
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||
"prettier.htmlWhitespaceSensitivity": "ignore",
|
||||
"prettier.vueIndentScriptAndStyle": true,
|
||||
"docthis.authorName": "https://vue-admin-beautiful.com",
|
||||
"docthis.includeAuthorTag": true,
|
||||
"docthis.includeDescriptionTag": true,
|
||||
"docthis.enableHungarianNotationEvaluation": true,
|
||||
"docthis.inferTypesFromNames": true,
|
||||
"vetur.format.defaultFormatter.html": "prettier",
|
||||
"files.autoSave": "onFocusChange",
|
||||
"path-intellisense.mappings": {
|
||||
"@": "${workspaceRoot}/src"
|
||||
},
|
||||
"files.eol": "\n"
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 good luck
|
||||
|
||||
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,210 @@
|
|||
[简体中文](./README.md) | English
|
||||
|
||||
<div align="center"><img width="200" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/logo/vab.png"/>
|
||||
<h1> vue-admin-better(element-ui) </h1>
|
||||
<p>The flying snow all over the sky is a flying note, playing out expectations with blessings. May the epidemic dissipate as soon as possible, may you no longer have regrets next year, may you be warm in winter, may you not be cold in spring, and may you have lights in the dark and an umbrella in the rain.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
[](https://github.com/chuzhixin/vue-admin-beautiful)
|
||||
[](https://gitee.com/chu1204505056/vue-admin-better)
|
||||
[](https://en.wikipedia.org/wiki/MIT_License)
|
||||
|
||||
---
|
||||
|
||||
# 🎉 Characteristic
|
||||
|
||||
- 💪 40 + high quality single page
|
||||
- 💅 RBAC model + JWT permission control
|
||||
- 🌍 100000 + practical application of the project
|
||||
- 👏 Good type definition
|
||||
- 🥳 The open source version supports free commercial use
|
||||
- 🚀 Cross platform PC, mobile terminal and tablet
|
||||
- 📦 Back end route dynamic rendering
|
||||
|
||||
## 🌐 Address
|
||||
|
||||
- [🎉 Vue2. X + element UI (free commercial, PC, tablet and mobile phone supported)](https://vue-admin-beautiful.com/vue-admin-beautiful-element/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [⚡️ Vue3. X + element plus (alpha version, free commercial, supporting PC, tablet and mobile phone)](https://vue-admin-beautiful.com/vue-admin-beautiful-element-plus/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [⚡️ Vue3. X + ant design Vue (beta version, free commercial, supporting PC, tablet and mobile phone)](https://vue-admin-beautiful.com/vue-admin-beautiful-antdv/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [⚡️ vue3.x + vite + vue-admin-arco](https://vue-admin-beautiful.com/vue-admin-arco/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [🚀 Admin Pro demo address (vue2.x paid version, supporting PC, tablet and mobile phone)](https://vue-admin-beautiful.com/admin-pro/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [🚀 Admin plus demo address (vue3.x paid version, supporting PC, tablet and mobile phone)](https://vue-admin-beautiful.com/admin-plus/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [📌 Pro and plus purchase address authorization](https://vue-admin-beautiful.com/authorization/)
|
||||
|
||||
- [🌐 Github Warehouse address](https://github.com/chuzhixin/vue-admin-beautiful?utm_source=gold_browser_extension)
|
||||
|
||||
- [🌐 Gitee Warehouse address](https://gitee.com/chu1204505056/vue-admin-better?_from=gitee_search)
|
||||
|
||||
- Recently, the VAB official website has been frequently attacked by DDoS. We have taken relevant preventive measures.
|
||||
If the website cannot be accessed, please visit the backup address
|
||||
|
||||
## 🌐 Backup address (support automatic update of HTTPS website)
|
||||
|
||||
- [🚀 Admin Pro demo address (paid version, supporting PC, tablet and mobile phone)](https://chu1204505056.gitee.io/admin-pro/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
- [🚀 Admin plus demo address (vue3. X paid version, supporting PC, tablet and mobile phone)](https://chu1204505056.gitee.io/admin-plus/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
|
||||
## 📦️ Desktop applications
|
||||
|
||||
- [Admin Pro](https://gitee.com/chu1204505056/microsoft-store/raw/master/AdminPlus.zip)
|
||||
- [Admin Plus](https://gitee.com/chu1204505056/microsoft-store/raw/master/AdminPlus.zip)
|
||||
|
||||
## 🌱 Vue3.x vue3.0-antdv [Click switch branch](https://github.com/chuzhixin/vue-admin-better/tree/vue3.0-antdv)
|
||||
|
||||
```bash
|
||||
git clone -b vue3.0-antdv https://github.com/chuzhixin/vue-admin-better.git
|
||||
npm i --registry=http://mirrors.cloud.tencent.com/npm/
|
||||
npm run serve
|
||||
```
|
||||
|
||||
## 🌱 Vue2.xmain [Click switch branch](https://github.com/chuzhixin/vue-admin-better/tree/master)
|
||||
|
||||
```bash
|
||||
git clone -b master https://github.com/chuzhixin/vue-admin-better.git
|
||||
npm i --registry=http://mirrors.cloud.tencent.com/npm/
|
||||
npm run serve
|
||||
```
|
||||
|
||||
## 🍻 Front end discussion QQ group
|
||||
|
||||
- Let's have a cup of coffee, and then contact QQ 783963206 to invite you to the discussion group. Because of the large
|
||||
number of users, if you haven't passed a friend's request, please contact Alipay on the Alipay payment page, whether
|
||||
you invite or not, you can enjoy the open source code, thank you for your support and trust, and provide the
|
||||
vue-admin-beautifu basic version in the group. Automatic configuration tutorial of development tools and project
|
||||
development documents.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<!-- <td>
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/hbm.jpg">
|
||||
</td> -->
|
||||
<td>
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/zfb_kf.jpg">
|
||||
</td>
|
||||
<td>
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-2.jpg">
|
||||
</td>
|
||||
<td>
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-3.jpg">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 🙈 We promise to sponsor open source projects regularly (thank giant)
|
||||
|
||||
<a title="vue" href="https://opencollective.com/vuejs" target="_blank">
|
||||
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/vue.png"/>
|
||||
</a>
|
||||
<a title="element-plus" href="https://opencollective.com/element-plus" target="_blank">
|
||||
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/element-plus.png"/>
|
||||
</a>
|
||||
<a title="ant-design-vue" href="https://opencollective.com/ant-design-vue" target="_blank">
|
||||
<img width="64px" src="https://images.opencollective.com/ant-design-vue/2ec179b/logo/256.png"/>
|
||||
</a>
|
||||
|
||||
## 🎨 Acknowledge
|
||||
|
||||
| Project |
|
||||
|------------------------------------------------------------------|
|
||||
| [vue](https://github.com/vuejs/vue) |
|
||||
| [element-ui](https://github.com/ElemeFE/element) |
|
||||
| [element-plus](https://github.com/element-plus/element-plus) |
|
||||
| [ant-design-vue](https://github.com/vueComponent/ant-design-vue) |
|
||||
| [mock](https://github.com/nuysoft/Mock) |
|
||||
| [axios](https://github.com/axios/axios) |
|
||||
|
||||
## 👷 Outstanding contributors to the framework (in no order)
|
||||
|
||||
<a href="https://github.com/buuing" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/36689704?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/hipi" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/22478003?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/fwfmiao" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/29328241?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/hdtopku" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/14859466?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/shaonialife" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/16135960?s=50"/>
|
||||
</a>
|
||||
|
||||
## 📌 Advantages and precautions
|
||||
|
||||
```
|
||||
Compared with other open source admin frameworks, it has the following advantages:
|
||||
1. Support the front-end control routing permission intelligence and the back-end control routing permission all mode
|
||||
2. It is known that the open source Vue admin framework is the first to support the automatic generation and export function of mock
|
||||
3. More than 50 global fine configurations are provided
|
||||
4. Support SCSS automatic sorting and eslint automatic repair
|
||||
5. Axios fine encapsulation supports multiple data sources, multiple successful code arrays, and application / JSON; charset=UTF-8、application/x-www-form-urlencoded; Charset = UTF-8 multiple parameter transfer modes
|
||||
6. Support login RSA encryption
|
||||
7. Support packaging to automatically generate 7z compressed packages
|
||||
8. Support errorlog error interception
|
||||
9. Support multi theme and multi layout switching
|
||||
Precautions for use:
|
||||
1. The project uses lf line feed instead of CRLF line feed by default. When creating a new file, please pay attention to selecting the file line feed
|
||||
2. The project uses the strictest eslint verification specification (plugin: Vue / recommended) by default. Before using it, it is recommended to configure the development tool to realize automatic repair (vscode development is recommended)
|
||||
3. The project uses the MIT open source agreement with the broadest requirements, and the MIT open source agreement can be used for free
|
||||
|
||||
```
|
||||
|
||||
## 💚 Suitable for people
|
||||
|
||||
- I am developing and want to use element UI / element plus, with 1 year of front-end development experience +.
|
||||
- Familiar with vue.js technology stack and developed several practical projects with it.
|
||||
- Students who are interested in principle and technology and want to improve.
|
||||
|
||||
## 🎉 Function map
|
||||
|
||||

|
||||
|
||||
## 🗃️ design sketch
|
||||
|
||||
The following is a screenshot of the pro version:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/2.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/6.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/8.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/9.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/3.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/5.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 📄 Commercial considerations
|
||||
|
||||
This project can be used for commercial purposes free of charge. Please abide by the MIT agreement and keep the author's
|
||||
technical support statement. For customized source code copyright information, please contact customer service QQ
|
||||
783963206.
|
||||
|
||||
<!-- ## 严正声明
|
||||
|
||||
近期发现不少游手好闲之人有组织有预谋的利用码云、知乎、掘金等网站可用国外非法网站提供的匿名手机号注册的账号 bug 冒充 vab 去攻击 vue-element-admin,iview-admin,若依,d2-admin,ant-design-vue 的行为,恶意制造对立,试图让其他开源作者卷入其中,对各位开源作者造成的影响我们深表歉意,我们欢迎 vab 的用户去体验其他更优秀的框架,vue-admin-beautiful 走到今天实属不易,被人冒充,被人发帖诋毁,被人故意发布错误言论假装发帖表扬实则为我们招骂,无意动任何人的奶酪,从 2020 年至今坚持全职维护已过一年时间,说实在的我们靠技术生存并不丢人吧,一年来感谢 vab 的用户对我们不离不弃,也希望大家越来越好,加油! -->
|
||||
238
README.md
|
|
@ -1,139 +1,172 @@
|
|||
<div align="center"><img width="200" src="https://gitee.com/chu1204505056/vue-admin-beautiful/raw/master/src/colorfulIcon/svg/vab.svg"/>
|
||||
<h1> vue-admin-beautiful(ant-design-vue) </h1>
|
||||
<div>
|
||||
|
||||
简体中文 | [English](./README.en.md)
|
||||
|
||||
<div align="center"><img width="200" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/logo/vab.png"/>
|
||||
<h1> vue-admin-better</h1>
|
||||
<p>瑞雪兆丰年,红梅报新春,愿您新的一年平安喜乐,万事顺意,所得皆所愿!</p>
|
||||
</div>
|
||||
|
||||
[](https://vue-admin-beautiful.com)
|
||||
[](https://github.com/chuzhixin/vue-admin-beautiful)
|
||||
[](https://gitee.com/chu1204505056/vue-admin-beautiful)
|
||||
[](https://gitee.com/chu1204505056/vue-admin-better)
|
||||
[](https://en.wikipedia.org/wiki/MIT_License)
|
||||
|
||||
## 地址
|
||||
---
|
||||
|
||||
- [🎉 vue2.x + element-ui(免费商用,支持 PC、平板、手机)](http://vue-admin-beautiful.com/vue-admin-beautiful-element/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
## 🎉 全新版本
|
||||
|
||||
- [⚡️ vue3.x + element-plus(alpha 版本,免费商用,支持 PC、平板、手机)](http://vue-admin-beautiful.com/vue-admin-beautiful-element-plus/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
基于 vite5.x + vue3.x + arco-design2.x 全新的前端框架 vue-admin-arco, 欢迎点击查看或试用 👏🏻👏🏻👏🏻
|
||||
|
||||
- [⚡️ vue3.x + ant-design-vue(beta 版本,免费商用,支持 PC、平板、手机)](http://vue-admin-beautiful.com/vue-admin-beautiful-antdv/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
[开源地址](https://github.com/chuzhixin/vue-admin-arco) | [演示地址](https://vue-admin-beautiful.com/vue-admin-arco)
|
||||
|
||||
- [🚀 admin pro 演示地址(付费版本,支持 PC、平板、手机)](http://vue-admin-beautiful.com/admin-pro/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
## 🎉 特性
|
||||
|
||||
- [🚀 admin plus 演示地址(vue3.x 付费版本,支持 PC、平板、手机)](http://vue-admin-beautiful.com/admin-plus/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
- 💪 40+高质量单页
|
||||
- 💅 RBAC 模型 + JWT 权限控制
|
||||
- 🌍 10 万+ 项目实际应用
|
||||
- 👏 良好的类型定义
|
||||
- 🥳 开源版本支持免费商用
|
||||
- 🚀 跨平台 PC、手机端、平板
|
||||
- 📦️ 后端路由动态渲染
|
||||
|
||||
- [📌 pro 及 plus 购买地址 authorization](http://vue-admin-beautiful.com/authorization/)
|
||||
## 🌐 付费版演示地址
|
||||
|
||||
- [🌐 github 仓库地址](https://github.com/chuzhixin/vue-admin-beautiful?utm_source=gold_browser_extension)
|
||||
- [🚀 Vue Admin Pro 演示地址(vue2.x + element-ui 2.x 付费版本,支持 PC、平板、手机)](https://vue-admin-beautiful.com/admin-pro/)
|
||||
|
||||
- [🌐 码云仓库地址](https://gitee.com/chu1204505056/vue-admin-beautiful?_from=gitee_search)
|
||||
- [🚀 Vue Admin Plus 演示地址(vue3.x + element-plus 2.x 付费版本,支持 PC、平板、手机)](https://vue-admin-beautiful.com/admin-plus/)
|
||||
|
||||
- 近期 vab 官网频繁遭到 ddos 攻击,我们已采取相关防范措施,如网站无法访问请访问备份地址
|
||||
- [🚀 Vue Shop Vite 演示地址(vue3.x + vite 5.x + element-plus 2.x 付费版本,支持 PC、平板、手机)](https://vue-admin-beautiful.com/shop-vite/)
|
||||
|
||||
## 备份地址
|
||||
- [📌 Vue Admin Pro 及 Vue Admin Plus 购买地址](https://vue-admin-beautiful.com/authorization/)
|
||||
|
||||
- [🚀 admin pro 演示地址(付费版本,支持 PC、平板、手机)](https://chu1204505056.gitee.io/admin-pro/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
- [📌 Vue Shop Vite 购买地址](https://vue-admin-beautiful.com/authorization/shop-vite.html)
|
||||
|
||||
- [🚀 admin plus 演示地址(vue3.x 付费版本,支持 PC、平板、手机)](https://chu1204505056.gitee.io/admin-plus/?hmsr=github&hmpl=&hmcu=&hmkw=&hmci=)
|
||||
## 🌐 免费版演示地址
|
||||
|
||||
## vue-admin-beautiful 前端讨论 QQ 群
|
||||
- [🎉 Vue Admin Better (vue2.x + element-ui 免费商用,支持 PC、平板、手机)](https://vue-admin-beautiful.com/vue-admin-beautiful-element/)
|
||||
|
||||
- 请我们喝杯咖啡,支付后联系 QQ 783963206 邀请您进入讨论群(由于用户数较多,如果您打赏后未通过好友请求,请在支付宝支付页面选择联系商家),不管您请还是不请,您都可以享受到开源的代码,感谢您的支持和信任,群内提供 vue-admin-beautifu 基础版本、开发工具自动配置教程及项目开发文档。
|
||||
- [⚡️ vue3.x + element-plus(alpha 版本,免费商用,支持 PC、平板、手机)](https://vue-admin-beautiful.com/vue-admin-beautiful-element-plus/)
|
||||
|
||||
- [⚡️ Vue Admin Ant (vue3.x + ant-design-vue 免费商用,支持 PC、平板、手机)](https://vue-admin-beautiful.com/vue-admin-beautiful-antdv/)
|
||||
|
||||
- [⚡️ Vue Admin Arco (vue3.x + vite5.x + arco2.x 免费商用,支持 PC)](https://vue-admin-beautiful.com/vue-admin-arco/)
|
||||
|
||||
## 🌐 仓库地址
|
||||
|
||||
- [🌐 vue2.x github 仓库地址](https://github.com/chuzhixin/vue-admin-beautiful?utm_source=gold_browser_extension)
|
||||
|
||||
- [🌐 vue3.x github 仓库地址](https://github.com/chuzhixin/vue-admin-arco?utm_source=gold_browser_extension)
|
||||
|
||||
- [🌐 vue2.x 码云仓库地址](https://gitee.com/chu1204505056/vue-admin-better?_from=gitee_search)
|
||||
|
||||
- [🌐 vue3.x 码云仓库地址](https://gitee.com/chu1204505056/vue-admin-arco?_from=gitee_search)
|
||||
|
||||
## 🍻 前端讨论 QQ 群
|
||||
|
||||
- 请我们喝杯咖啡,打赏后联系 QQ 783963206 邀请您进入讨论群(由于用户数较多,如果您打赏后未通过好友请求,请联系商家),不管您请还是不请,您都可以享受到开源的代码,感谢您的支持和信任,群内提供
|
||||
vue-admin-better 基础版本、开发工具自动配置教程及项目开发文档。
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img width="200px" src="https://gitee.com/chu1204505056/image/raw/master/zfb_kf.jpg">
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/zfb_kf.jpg">
|
||||
</td>
|
||||
<td>
|
||||
<img width="200px" src="https://gitee.com/chu1204505056/image/raw/master/qq_group/vab-2.jpg">
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-2.jpg">
|
||||
</td>
|
||||
<td>
|
||||
<img width="200px" src="https://gitee.com/chu1204505056/image/raw/master/qq_group/vab-3.jpg">
|
||||
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-3.jpg">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 🌱 vue3.x vue3.0-antdv 分支(ant-design-vue)[点击切换分支](https://github.com/chuzhixin/vue-admin-beautiful-pro/tree/vue3.0-antdv)
|
||||
## 📦️ 桌面应用程序
|
||||
|
||||
- [Admin Pro](https://gitee.com/chu1204505056/microsoft-store/raw/master/AdminPlus.zip)
|
||||
- [Admin Plus](https://gitee.com/chu1204505056/microsoft-store/raw/master/AdminPlus.zip)
|
||||
|
||||
## 🌱 vue3.x vue3.0-antdv 分支(ant-design-vue)[点击切换分支](https://github.com/chuzhixin/vue-admin-better/tree/vue3.0-antdv)
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone -b vue3.0-antdv https://github.com/chuzhixin/vue-admin-beautiful.git
|
||||
# 进入项目目录
|
||||
cd vue-admin-beautiful-pro
|
||||
git clone -b vue3.0-antdv https://github.com/chuzhixin/vue-admin-better.git
|
||||
# 安装依赖
|
||||
npm i
|
||||
npm i --registry=http://mirrors.cloud.tencent.com/npm/
|
||||
# 本地开发 启动项目
|
||||
npm run serve
|
||||
```
|
||||
|
||||
## 🌱vue2.x master 分支(element-ui)[点击切换分支](https://github.com/chuzhixin/vue-admin-beautiful-pro/tree/master)
|
||||
## 🌱 vue3.x arco-design [点击切换仓库](https://github.com/chuzhixin/vue-admin-arco)
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone -b master https://github.com/chuzhixin/vue-admin-beautiful.git
|
||||
# 进入项目目录
|
||||
cd vue-admin-beautiful-pro
|
||||
git clonehttps://github.com/chuzhixin/vue-admin-arco.git
|
||||
# 安装依赖
|
||||
npm i
|
||||
npm i --registry=http://mirrors.cloud.tencent.com/npm/
|
||||
# 本地开发 启动项目
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 🌱vue2.x master 分支(element-ui)[点击切换分支](https://github.com/chuzhixin/vue-admin-better/tree/master)
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone -b master https://github.com/chuzhixin/vue-admin-better.git
|
||||
# 安装依赖
|
||||
npm i --registry=http://mirrors.cloud.tencent.com/npm/
|
||||
# 本地开发 启动项目
|
||||
npm run serve
|
||||
```
|
||||
|
||||
## 友情链接
|
||||
## 🔊 友情链接
|
||||
|
||||
- [uView 文档(超棒的移动跨端框架,文档详细,上手容易)](https://uviewui.com/)
|
||||
- [OPSLI 基于 vue-admin-better 开源版的最佳实践](https://github.com/hiparker/opsli-boot)
|
||||
|
||||
- [uView 开源地址(uni-app 生态优秀的 UI 框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水)](https://github.com/YanxinNet/uView/)
|
||||
- [uView uni-app 生态最优秀的 UI 框架](https://github.com/YanxinNet/uView/)
|
||||
|
||||
- [Element UI 表单设计及代码生成器(可视化表单设计器,一键生成 element 表单)](https://github.com/JakHuang/form-generator/)
|
||||
- [form-generator Element 表单设计代码生成器](https://github.com/JakHuang/form-generator/)
|
||||
|
||||
- [luch-request(基于 Promise 开发的 uni-app 跨平台、项目级别的请求库)](https://www.quanzhan.co/luch-request/)
|
||||
- [wangEditor 国产最强开源富文本编辑](https://github.com/wangeditor-team/wangEditor)
|
||||
|
||||
## 我们承诺将定期赞助的开源项目(感谢巨人)
|
||||
## 🙈 赞助
|
||||
|
||||
<a title="vue" href="https://cn.vuejs.org/" target="_blank">
|
||||
<img width="64px" src="https://gitee.com/chu1204505056/image/raw/master/vue.png"/>
|
||||
- 如果您觉得 vue admin better 帮到了您 ,如果情况允许,您可以选择赞助以下项目
|
||||
|
||||
<a title="vue" href="https://opencollective.com/vuejs" target="_blank">
|
||||
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/vue.png"/>
|
||||
</a>
|
||||
|
||||
<a title="ant-design-vue" href="https://github.com/vueComponent/ant-design-vue#backers" target="_blank">
|
||||
<img width="64px" src="https://gitee.com/chu1204505056/image/raw/master/antdv.svg"/>
|
||||
</a>
|
||||
|
||||
<a title="element-plus" href="https://opencollective.com/element-plus" target="_blank">
|
||||
<img width="64px" src="https://gitee.com/chu1204505056/image/raw/master/element-plus.png"/>
|
||||
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/element-plus.png"/>
|
||||
</a>
|
||||
<a title="ant-design-vue" href="https://opencollective.com/ant-design-vue" target="_blank">
|
||||
<img width="64px" src="https://images.opencollective.com/ant-design-vue/2ec179b/logo/256.png"/>
|
||||
</a>
|
||||
|
||||
## 鸣谢
|
||||
|
||||
| Project |
|
||||
| ---------------------------------------------------------------- |
|
||||
| [vue](https://github.com/vuejs/vue) |
|
||||
| [element-ui](https://github.com/ElemeFE/element) |
|
||||
| [element-plus](https://github.com/element-plus/element-plus) |
|
||||
| [ant-design-vue](https://github.com/vueComponent/ant-design-vue) |
|
||||
| [mock](https://github.com/nuysoft/Mock) |
|
||||
| [axios](https://github.com/axios/axios) |
|
||||
|
||||
## 框架杰出贡献者(排名不分先后)
|
||||
## 👷 框架杰出贡献者
|
||||
|
||||
<a href="https://github.com/fwfmiao" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/29328241?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/buuing" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/36689704?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/hipi" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/22478003?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/fwfmiao" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/29328241?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/hdtopku" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/14859466?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/shaonialife" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/16135960?s=50"/>
|
||||
</a>
|
||||
<a href="https://github.com/1511578084" target="_blank">
|
||||
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/24790218?s=50"/>
|
||||
</a>
|
||||
|
||||
## 优势及注意事项
|
||||
## 📌 优势及注意事项
|
||||
|
||||
```
|
||||
vue-admin-beautiful-pro 对比其他开源 admin 框架有如下优势:
|
||||
对比其他开源 admin 框架有如下优势:
|
||||
1. 支持前端控制路由权限 intelligence、后端控制路由权限 all 模式
|
||||
2. 已知开源 vue admin 框架中首家支持 mock 自动生成自动导出功能
|
||||
3. 提供 50 余项全局精细化配置
|
||||
|
|
@ -144,72 +177,97 @@ vue-admin-beautiful-pro 对比其他开源 admin 框架有如下优势:
|
|||
8. 支持errorlog错误拦截
|
||||
9. 支持多主题、多布局切换
|
||||
|
||||
vue-admin-beautiful-pro 使用注意事项:
|
||||
使用注意事项:
|
||||
1. 项目默认使用lf换行符而非crlf换行符,新建文件时请注意选择文件换行符
|
||||
2. 项目默认使用的最严格的eslint校验规范(plugin:vue/recommended),使用之前建议配置开发工具实现自动修复(建议使用vscode开发)
|
||||
3. 项目使用的是要求最宽泛的MIT开源协议,保留MIT开源协议即可免费商用
|
||||
|
||||
```
|
||||
|
||||
## 适合人群
|
||||
## 💚 适合人群
|
||||
|
||||
- 正在以及想使用 element-ui/element-plus 开发,前端开发经验 1 年+。
|
||||
- 熟悉 Vue.js 技术栈,使用它开发过几个实际项目。
|
||||
- 对原理技术感兴趣,想进阶和提升的同学。
|
||||
|
||||
## 功能地图:
|
||||
## 🎨 Star
|
||||
|
||||

|
||||
[](https://github.com/chuzhixin/vue-admin-better/stargazers)
|
||||
|
||||
## 特性:
|
||||
## ✨ Fork
|
||||
|
||||
- 支持 PC、手机端、平板;
|
||||
- 提供超过 50 余项全局精细化配置;
|
||||
- 支持后端渲染动态路由
|
||||
- 拥有完整的登录鉴权和前后端多种配置的动态路由流程
|
||||
- 支持前端控制路由权限 intelligence、后端控制路由权限 all 模式
|
||||
- 支持 mock 自动生成自动导出功能
|
||||
- 支持 scss 自动排序,eslint 自动修复
|
||||
- 支持登录 RSA 加密
|
||||
- 支持打包自动生成 7Z 压缩包以及自动化部署
|
||||
- 支持 errorlog 错误拦截
|
||||
- 支持多主题、多布局切换
|
||||
[](https://github.com/chuzhixin/vue-admin-better/network/members)
|
||||
|
||||
## 效果图
|
||||
## 🎉 功能地图
|
||||
|
||||

|
||||
|
||||
## 🗃️ 效果图
|
||||
|
||||
以下是截取的是 pro 版的效果图展示:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://gitee.com/chu1204505056/image/raw/master/2.png">
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/2.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://gitee.com/chu1204505056/image/raw/master/6.png">
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/6.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://gitee.com/chu1204505056/image/raw/master/8.png">
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/8.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://gitee.com/chu1204505056/image/raw/master/9.png">
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/9.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://gitee.com/chu1204505056/image/raw/master/3.png">
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/3.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://gitee.com/chu1204505056/image/raw/master/5.png">
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/5.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 商用注意事项
|
||||
以下是截取的是 shop 版的效果图展示:
|
||||
|
||||
此项目可免费用于商业用途,请遵守 MIT 协议并保留作者技术支持声明,如需自定义版权信息请联系客服 QQ 783963206。
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/16.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/17.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/18.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/19.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/20.png">
|
||||
</td>
|
||||
<td>
|
||||
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/21.png">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ## 严正声明
|
||||
## 📄 商用注意事项
|
||||
|
||||
近期发现不少游手好闲之人有组织有预谋的利用码云、知乎、掘金等网站可用国外非法网站提供的匿名手机号注册的账号 bug 冒充 vab 去攻击 vue-element-admin,iview-admin,若依,d2-admin,ant-design-vue 的行为,恶意制造对立,试图让其他开源作者卷入其中,对各位开源作者造成的影响我们深表歉意,我们欢迎 vab 的用户去体验其他更优秀的框架,vue-admin-beautiful 走到今天实属不易,被人冒充,被人发帖诋毁,被人故意发布错误言论假装发帖表扬实则为我们招骂,无意动任何人的奶酪,从 2020 年至今坚持全职维护已过一年时间,说实在的我们靠技术生存并不丢人吧,一年来感谢 vab 的用户对我们不离不弃,也希望大家越来越好,加油! -->
|
||||
开源版本可免费用于商业用途,如果方便就留个 Star 吧
|
||||
|
||||
<!-- ,请遵守 MIT 协议并保留作者技术支持声明,当然如果不愿意保留可以删掉,毕竟我也拿您没办法,能帮到您也当是给自己积德了,至于[Admin](https://vue-admin-beautiful.com/admin-plus/)、[Shop](https://vue-admin-beautiful.com/shop-vite/) 的付费版本的相关说明如下:
|
||||
本人只参与了前期小部分的开发,所以不必跟开源版做对比,同事的代码功底比我好太多,我自愧不如,关于买这件事,没有强买强卖,您愿意买就买,不愿意买就忽略。我们不高尚,写代码就是为了养家糊口,不是为了用爱发电。这几年看到那么多开源项目借鉴了我们付费版本的布局、主题配置的灵感和创意,一开始我是鄙视的,现在还好状态调整过来了,能够被借鉴,被讨论恰好说明了我们的产品有价值,为了产品卖的更好我们也必须更加用心的去维护付费版本以保持我们产品的竞争力。
|
||||
当然,最后还有几句话不得不说,身处互联网由盛转衰的大变革的洪流中,能活下来就已经是千难万难了,希望所有的程序员哥哥姐姐们,早日实现自己的梦想,完成自己的心愿,也想对刚要毕业准备做一名程序员的学弟学妹们说几句,互联网行业没有你们想象的那么高大上,如果想成为一名程序员那就做好加班的准备,如果有更好的选择那就别选这个行业了。 -->
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* @author https://vue-admin-beautiful.com (不想保留author可删除)
|
||||
* @description babel.config
|
||||
*/
|
||||
module.exports = {
|
||||
presets: ['@vue/cli-plugin-babel/preset'],
|
||||
}
|
||||
|
|
|
|||
10
deploy.sh
|
|
@ -1,3 +1,4 @@
|
|||
#强制推送
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
npm run build
|
||||
|
|
@ -6,10 +7,11 @@ touch .nojekyll
|
|||
git init
|
||||
git add -A
|
||||
git commit -m 'deploy'
|
||||
git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-beautiful-mini.git" master:gh-pages
|
||||
git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-beautiful-antdv.git" master:gh-pages
|
||||
start "https://gitee.com/chu1204505056/vue-admin-beautiful-mini/pages"
|
||||
start "https://gitee.com/chu1204505056/vue-admin-beautiful-antdv/pages"
|
||||
git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-beautiful.git" master:gh-pages
|
||||
git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-beautiful-element.git" master:gh-pages
|
||||
start "https://gitee.com/chu1204505056/vue-admin-beautiful/pages"
|
||||
start "https://gitee.com/chu1204505056/vue-admin-beautiful-element/pages"
|
||||
git push -f "https://${access_token}@github.com/chuzhixin/vue-admin-beautiful.git" master:gh-pages
|
||||
cd -
|
||||
exec /bin/bash
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import permissions from './permissions'
|
||||
|
||||
const install = function (Vue) {
|
||||
Vue.directive('permissions', permissions)
|
||||
}
|
||||
|
||||
if (window.Vue) {
|
||||
window['permissions'] = permissions
|
||||
Vue.use(install)
|
||||
}
|
||||
|
||||
permissions.install = install
|
||||
export default permissions
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import store from '@/store'
|
||||
|
||||
export default {
|
||||
inserted(element, binding) {
|
||||
const { value } = binding
|
||||
const permissions = store.getters['user/permissions']
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const hasPermission = permissions.some((role) => value.includes(role))
|
||||
if (!hasPermission)
|
||||
element.parentNode && element.parentNode.removeChild(element)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<img
|
||||
v-if="isExternal"
|
||||
:src="styleExternalIcon"
|
||||
class="svg-external-icon svg-icon"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'VabColorfulIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return isExternal(this.iconClass)
|
||||
},
|
||||
iconName() {
|
||||
return `#colorful-icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
},
|
||||
styleExternalIcon() {
|
||||
return this.iconClass
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
overflow: hidden;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.svg-external-icon {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<div v-if="errorLogs.length > 0">
|
||||
<el-badge
|
||||
:value="errorLogs.length"
|
||||
@click.native="dialogTableVisible = true"
|
||||
>
|
||||
<el-button type="danger">
|
||||
<vab-icon :icon="['fas', 'bug']" />
|
||||
</el-button>
|
||||
</el-badge>
|
||||
|
||||
<el-dialog
|
||||
:visible.sync="dialogTableVisible"
|
||||
append-to-body
|
||||
title="vue-admin-better异常捕获(温馨提示:错误必须解决)"
|
||||
width="70%"
|
||||
>
|
||||
<el-table :data="errorLogs">
|
||||
<el-table-column label="报错路由">
|
||||
<template slot-scope="{ row }">
|
||||
<a :href="row.url" target="_blank">
|
||||
<el-tag type="success">{{ row.url }}</el-tag>
|
||||
</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="错误信息">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag type="danger">{{ decodeUnicode(row.err.message) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="错误详情" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-popover placement="top-start" trigger="hover">
|
||||
<div style="color: red">
|
||||
{{ scope.row.err.stack }}
|
||||
</div>
|
||||
<el-button slot="reference">查看</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="380">
|
||||
<template slot-scope="{ row }">
|
||||
<a
|
||||
v-for="(item, index) in searchList"
|
||||
:key="index"
|
||||
:href="item.url + decodeUnicode(row.err.message)"
|
||||
target="_blank"
|
||||
>
|
||||
<el-button style="margin-left: 5px" type="primary">
|
||||
<vab-icon :icon="['fas', 'search']" />
|
||||
{{ item.title }}
|
||||
</el-button>
|
||||
</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogTableVisible = false">取 消</el-button>
|
||||
<el-button icon="el-icon-delete" type="danger" @click="clearAll">
|
||||
暂不显示
|
||||
</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { abbreviation, title } from '@/config'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'VabErrorLog',
|
||||
|
||||
data() {
|
||||
return {
|
||||
dialogTableVisible: false,
|
||||
title: title,
|
||||
abbreviation: abbreviation,
|
||||
searchList: [
|
||||
{
|
||||
title: '百度搜索',
|
||||
url: 'https://www.baidu.com/baidu?wd=',
|
||||
},
|
||||
{
|
||||
title: '谷歌搜索',
|
||||
url: 'https://www.google.com/search?q=',
|
||||
},
|
||||
{
|
||||
title: 'Magi搜索',
|
||||
url: 'https://magi.com/search?q=',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
errorLogs: 'errorLog/errorLogs',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
clearAll() {
|
||||
this.dialogTableVisible = false
|
||||
this.$store.dispatch('errorLog/clearErrorLog')
|
||||
},
|
||||
decodeUnicode(str) {
|
||||
str = str.replace(/\\/g, '%')
|
||||
str = unescape(str)
|
||||
str = str.replace(/%/g, '\\')
|
||||
str = str.replace(/\\/g, '')
|
||||
return str
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep {
|
||||
.el-badge {
|
||||
.el-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<span :title="isFullscreen ? '退出全屏' : '进入全屏'">
|
||||
<vab-icon
|
||||
:icon="[
|
||||
'fas',
|
||||
isFullscreen ? 'compress-arrows-alt' : 'expand-arrows-alt',
|
||||
]"
|
||||
@click="click"
|
||||
></vab-icon>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import screenfull from 'screenfull'
|
||||
|
||||
export default {
|
||||
name: 'VabFullScreenBar',
|
||||
data() {
|
||||
return {
|
||||
isFullscreen: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.destroy()
|
||||
},
|
||||
methods: {
|
||||
click() {
|
||||
if (!screenfull.isEnabled) {
|
||||
this.$baseMessage('开启全屏失败', 'error')
|
||||
return false
|
||||
}
|
||||
screenfull.toggle()
|
||||
this.$emit('refresh')
|
||||
},
|
||||
change() {
|
||||
this.isFullscreen = screenfull.isFullscreen
|
||||
},
|
||||
init() {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.on('change', this.change)
|
||||
}
|
||||
},
|
||||
destroy() {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.off('change', this.change)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<a
|
||||
aria-label="View source on Github"
|
||||
class="github-corner"
|
||||
href="https://github.com/chuzhixin/vue-admin-beautiful"
|
||||
target="_blank"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="github-color"
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
width="80"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
<path
|
||||
class="octo-arm"
|
||||
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"
|
||||
/>
|
||||
<path
|
||||
class="octo-body"
|
||||
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"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabGithubCorner',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.github-corner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: $base-z-index - 3;
|
||||
|
||||
.octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out infinite;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.github-color {
|
||||
color: #fff;
|
||||
fill: $base-color-blue;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes octocat-wave {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(100deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<el-col :span="24">
|
||||
<div class="bottom-panel">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabQueryFormBottomPanel',
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<el-col :lg="span" :md="24" :sm="24" :xl="span" :xs="24">
|
||||
<div class="left-panel">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabQueryFormLeftPanel',
|
||||
props: {
|
||||
span: {
|
||||
type: Number,
|
||||
default: 14,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<el-col :lg="span" :md="24" :sm="24" :xl="span" :xs="24">
|
||||
<div class="right-panel">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabQueryFormRightPanel',
|
||||
props: {
|
||||
span: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<el-col :span="24">
|
||||
<div class="top-panel">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabQueryFormTopPanel',
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<el-row :gutter="0" class="vab-query-form">
|
||||
<slot></slot>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabQueryForm',
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@mixin panel {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.vab-query-form {
|
||||
margin-bottom: 10px;
|
||||
|
||||
::v-deep {
|
||||
.top-panel {
|
||||
@include panel;
|
||||
}
|
||||
|
||||
.bottom-panel {
|
||||
@include panel;
|
||||
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
@include panel;
|
||||
|
||||
> .el-button,
|
||||
.el-form-item {
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
@include panel;
|
||||
|
||||
justify-content: flex-end;
|
||||
|
||||
.el-form-item {
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="isExternal"
|
||||
:style="styleExternalIcon"
|
||||
class="svg-external-icon svg-icon"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
||||
<use :xlink:href="iconName" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils/validate'
|
||||
|
||||
export default {
|
||||
name: 'VabRemixIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return isExternal(this.iconClass)
|
||||
},
|
||||
iconName() {
|
||||
return `#remix-icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
},
|
||||
styleExternalIcon() {
|
||||
return {
|
||||
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`,
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.svg-icon {
|
||||
width: 1.125em;
|
||||
height: 1.125em;
|
||||
overflow: hidden;
|
||||
fill: currentColor;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.svg-external-icon {
|
||||
display: inline-block;
|
||||
background-color: currentColor;
|
||||
mask-size: cover !important;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<template>
|
||||
<el-menu-item :index="handlePath(routeChildren.path)" @click="handleLink">
|
||||
<vab-icon
|
||||
v-if="routeChildren.meta.icon"
|
||||
:icon="['fas', routeChildren.meta.icon]"
|
||||
class="vab-fas-icon"
|
||||
/>
|
||||
<span>{{ routeChildren.meta.title }}</span>
|
||||
<el-tag
|
||||
v-if="routeChildren.meta && routeChildren.meta.badge"
|
||||
effect="dark"
|
||||
type="danger"
|
||||
>
|
||||
{{ routeChildren.meta.badge }}
|
||||
</el-tag>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'VabMenuItem',
|
||||
props: {
|
||||
routeChildren: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
},
|
||||
},
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
},
|
||||
},
|
||||
fullPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handlePath(routePath) {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
if (isExternal(this.fullPath)) {
|
||||
return this.fullPath
|
||||
}
|
||||
return path.resolve(this.fullPath, routePath)
|
||||
},
|
||||
handleLink() {
|
||||
const routePath = this.routeChildren.path
|
||||
const target = this.routeChildren.meta.target
|
||||
|
||||
if (target === '_blank') {
|
||||
if (isExternal(routePath)) {
|
||||
window.open(routePath)
|
||||
} else if (isExternal(this.fullPath)) {
|
||||
window.open(this.fullPath)
|
||||
} else if (
|
||||
this.$route.path !== path.resolve(this.fullPath, routePath)
|
||||
) {
|
||||
let routeData = this.$router.resolve(
|
||||
path.resolve(this.fullPath, routePath)
|
||||
)
|
||||
window.open(routeData.href)
|
||||
}
|
||||
} else {
|
||||
if (isExternal(routePath)) {
|
||||
window.location.href = routePath
|
||||
} else if (isExternal(this.fullPath)) {
|
||||
window.location.href = this.fullPath
|
||||
} else if (
|
||||
this.$route.path !== path.resolve(this.fullPath, routePath)
|
||||
) {
|
||||
this.$router.push(path.resolve(this.fullPath, routePath))
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
<template>
|
||||
<component
|
||||
:is="menuComponent"
|
||||
v-if="!item.hidden"
|
||||
:full-path="fullPath"
|
||||
:item="item"
|
||||
:route-children="routeChildren"
|
||||
>
|
||||
<template v-if="item.children && item.children.length">
|
||||
<vab-side-bar-item
|
||||
v-for="route in item.children"
|
||||
:key="route.path"
|
||||
:full-path="handlePath(route.path)"
|
||||
:item="route"
|
||||
/>
|
||||
</template>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'VabSideBarItem',
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
fullPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
this.onlyOneChild = null
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
menuComponent() {
|
||||
if (
|
||||
this.handleChildren(this.item.children, this.item) &&
|
||||
(!this.routeChildren.children ||
|
||||
this.routeChildren.notShowChildren) &&
|
||||
!this.item.alwaysShow
|
||||
) {
|
||||
return 'VabMenuItem'
|
||||
} else {
|
||||
return 'VabSubmenu'
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChildren(children = [], parent) {
|
||||
if (children === null) children = []
|
||||
const showChildren = children.filter((item) => {
|
||||
if (item.hidden) {
|
||||
return false
|
||||
} else {
|
||||
this.routeChildren = item
|
||||
return true
|
||||
}
|
||||
})
|
||||
if (showChildren.length === 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (showChildren.length === 0) {
|
||||
this.routeChildren = {
|
||||
...parent,
|
||||
path: '',
|
||||
notShowChildren: true,
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
handlePath(routePath) {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
if (isExternal(this.fullPath)) {
|
||||
return this.fullPath
|
||||
}
|
||||
return path.resolve(this.fullPath, routePath)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vab-nav-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.el-tag {
|
||||
float: right;
|
||||
height: 16px;
|
||||
padding-right: 4px;
|
||||
padding-left: 4px;
|
||||
margin-top: calc((#{$base-menu-item-height} - 16px) / 2);
|
||||
line-height: 16px;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<el-submenu
|
||||
ref="subMenu"
|
||||
:index="handlePath(item.path)"
|
||||
:popper-append-to-body="false"
|
||||
>
|
||||
<template slot="title">
|
||||
<vab-icon
|
||||
v-if="item.meta && item.meta.icon"
|
||||
:icon="['fas', item.meta.icon]"
|
||||
class="vab-fas-icon"
|
||||
/>
|
||||
<vab-remix-icon
|
||||
v-if="item.meta && item.meta.remixIcon"
|
||||
:icon-class="item.meta.remixIcon"
|
||||
class="vab-remix-icon"
|
||||
/>
|
||||
<span>{{ item.meta.title }}</span>
|
||||
</template>
|
||||
<slot />
|
||||
</el-submenu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import path from 'path'
|
||||
|
||||
export default {
|
||||
name: 'VabSubmenu',
|
||||
props: {
|
||||
routeChildren: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
},
|
||||
},
|
||||
item: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
},
|
||||
},
|
||||
fullPath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handlePath(routePath) {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
if (isExternal(this.fullPath)) {
|
||||
return this.fullPath
|
||||
}
|
||||
return path.resolve(this.fullPath, routePath)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<template>
|
||||
<el-scrollbar :class="{ 'is-collapse': collapse }" class="side-bar-container">
|
||||
<vab-logo />
|
||||
<el-menu
|
||||
:active-text-color="variables['menu-color-active']"
|
||||
:background-color="variables['menu-background']"
|
||||
:collapse="collapse"
|
||||
:collapse-transition="false"
|
||||
:default-active="activeMenu"
|
||||
:default-openeds="defaultOpens"
|
||||
:text-color="variables['menu-color']"
|
||||
:unique-opened="uniqueOpened"
|
||||
mode="vertical"
|
||||
>
|
||||
<template v-for="route in routes">
|
||||
<vab-side-bar-item
|
||||
:key="route.path"
|
||||
:full-path="route.path"
|
||||
:item="route"
|
||||
/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<script>
|
||||
import variables from '@/styles/variables.scss'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { defaultOopeneds, uniqueOpened } from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'VabSideBar',
|
||||
data() {
|
||||
return {
|
||||
uniqueOpened,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
collapse: 'settings/collapse',
|
||||
routes: 'routes/routes',
|
||||
}),
|
||||
defaultOpens() {
|
||||
if (this.collapse) {
|
||||
}
|
||||
return defaultOopeneds
|
||||
},
|
||||
activeMenu() {
|
||||
const route = this.$route
|
||||
const { meta, path } = route
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
}
|
||||
return path
|
||||
},
|
||||
variables() {
|
||||
return variables
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@mixin active {
|
||||
&:hover {
|
||||
color: $base-color-white;
|
||||
background-color: $base-menu-background-active !important;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
color: $base-color-white;
|
||||
background-color: $base-menu-background-active !important;
|
||||
}
|
||||
}
|
||||
|
||||
.side-bar-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $base-z-index;
|
||||
width: $base-left-menu-width;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: $base-menu-background;
|
||||
box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
|
||||
transition: width $base-transition-time;
|
||||
|
||||
&.is-collapse {
|
||||
width: $base-left-menu-width-min;
|
||||
border-right: 0;
|
||||
|
||||
::v-deep {
|
||||
.el-menu {
|
||||
transition: width $base-transition-time;
|
||||
}
|
||||
|
||||
.el-menu--collapse {
|
||||
border-right: 0;
|
||||
|
||||
.el-submenu__icon-arrow {
|
||||
right: 10px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.el-scrollbar__wrap {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: 0;
|
||||
|
||||
.vab-fas-icon {
|
||||
padding-right: 3px;
|
||||
font-size: $base-font-size-default;
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
.vab-remix-icon {
|
||||
padding-right: 3px;
|
||||
font-size: $base-font-size-default + 2;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
height: $base-menu-item-height;
|
||||
line-height: $base-menu-item-height;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
@include active;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
<template>
|
||||
<div id="tabs-bar-container" class="tabs-bar-container">
|
||||
<el-tabs
|
||||
v-model="tabActive"
|
||||
class="tabs-content"
|
||||
type="card"
|
||||
@tab-click="handleTabClick"
|
||||
@tab-remove="handleTabRemove"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="item in visitedRoutes"
|
||||
:key="item.path"
|
||||
:closable="!isAffix(item)"
|
||||
:label="item.meta.title"
|
||||
:name="item.path"
|
||||
></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dropdown @command="handleCommand">
|
||||
<span style="cursor: pointer">
|
||||
更多操作
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown" class="tabs-more">
|
||||
<el-dropdown-item command="closeOtherstabs">
|
||||
<vab-icon :icon="['fas', 'times-circle']" />
|
||||
关闭其他
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="closeLefttabs">
|
||||
<vab-icon :icon="['fas', 'arrow-alt-circle-left']"></vab-icon>
|
||||
关闭左侧
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="closeRighttabs">
|
||||
<vab-icon :icon="['fas', 'arrow-alt-circle-right']"></vab-icon>
|
||||
关闭右侧
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="closeAlltabs">
|
||||
<vab-icon :icon="['fas', 'ban']"></vab-icon>
|
||||
关闭全部
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import path from 'path'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'VabTabsBar',
|
||||
data() {
|
||||
return {
|
||||
affixtabs: [],
|
||||
tabActive: '',
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({
|
||||
visitedRoutes: 'tabsBar/visitedRoutes',
|
||||
routes: 'routes/routes',
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler(route) {
|
||||
this.inittabs()
|
||||
this.addtabs()
|
||||
let tabActive = ''
|
||||
this.visitedRoutes.forEach((item, index) => {
|
||||
if (item.path === this.$route.path) {
|
||||
tabActive = item.path
|
||||
}
|
||||
})
|
||||
this.tabActive = tabActive
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
//console.log(this.visitedRoutes);
|
||||
},
|
||||
methods: {
|
||||
async handleTabRemove(tabActive) {
|
||||
let view
|
||||
this.visitedRoutes.forEach((item, index) => {
|
||||
if (tabActive == item.path) {
|
||||
view = item
|
||||
}
|
||||
})
|
||||
const { visitedRoutes } = await this.$store.dispatch(
|
||||
'tabsBar/delRoute',
|
||||
view
|
||||
)
|
||||
if (this.isActive(view)) {
|
||||
this.toLastTag(visitedRoutes, view)
|
||||
}
|
||||
},
|
||||
handleTabClick(tab) {
|
||||
const route = this.visitedRoutes.filter((item, index) => {
|
||||
if (tab.index == index) return item
|
||||
})[0]
|
||||
if (this.$route.path !== route.path) {
|
||||
this.$router.push({
|
||||
path: route.path,
|
||||
query: route.query,
|
||||
fullPath: route.fullPath,
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
isActive(route) {
|
||||
return route.path === this.$route.path
|
||||
},
|
||||
isAffix(tag) {
|
||||
return tag.meta && tag.meta.affix
|
||||
},
|
||||
filterAffixtabs(routes, basePath = '/') {
|
||||
let tabs = []
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path)
|
||||
tabs.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta },
|
||||
})
|
||||
}
|
||||
if (route.children) {
|
||||
const temptabs = this.filterAffixtabs(route.children, route.path)
|
||||
if (temptabs.length >= 1) {
|
||||
tabs = [...tabs, ...temptabs]
|
||||
}
|
||||
}
|
||||
})
|
||||
return tabs
|
||||
},
|
||||
inittabs() {
|
||||
const affixtabs = (this.affixtabs = this.filterAffixtabs(this.routes))
|
||||
for (const tag of affixtabs) {
|
||||
if (tag.name) {
|
||||
this.$store.dispatch('tabsBar/addVisitedRoute', tag)
|
||||
}
|
||||
}
|
||||
},
|
||||
addtabs() {
|
||||
const { name } = this.$route
|
||||
if (name) {
|
||||
this.$store.dispatch('tabsBar/addVisitedRoute', this.$route)
|
||||
}
|
||||
return false
|
||||
},
|
||||
handleCommand(command) {
|
||||
switch (command) {
|
||||
case 'refreshRoute':
|
||||
this.refreshRoute()
|
||||
break
|
||||
case 'closeOtherstabs':
|
||||
this.closeOtherstabs()
|
||||
break
|
||||
case 'closeLefttabs':
|
||||
this.closeLefttabs()
|
||||
break
|
||||
case 'closeRighttabs':
|
||||
this.closeRighttabs()
|
||||
break
|
||||
case 'closeAlltabs':
|
||||
this.closeAlltabs()
|
||||
break
|
||||
}
|
||||
},
|
||||
async refreshRoute() {
|
||||
this.$baseEventBus.$emit('reloadrouter-view')
|
||||
},
|
||||
async closeSelectedTag(view) {
|
||||
const { visitedRoutes } = await this.$store.dispatch(
|
||||
'tabsBar/delRoute',
|
||||
view
|
||||
)
|
||||
if (this.isActive(view)) {
|
||||
this.toLastTag(visitedRoutes, view)
|
||||
}
|
||||
},
|
||||
async closeOtherstabs() {
|
||||
const view = await this.toThisTag()
|
||||
await this.$store.dispatch('tabsBar/delOthersRoutes', view)
|
||||
},
|
||||
async closeLefttabs() {
|
||||
const view = await this.toThisTag()
|
||||
await this.$store.dispatch('tabsBar/delLeftRoutes', view)
|
||||
},
|
||||
async closeRighttabs() {
|
||||
const view = await this.toThisTag()
|
||||
await this.$store.dispatch('tabsBar/delRightRoutes', view)
|
||||
},
|
||||
async closeAlltabs() {
|
||||
const view = await this.toThisTag()
|
||||
const { visitedRoutes } = await this.$store.dispatch(
|
||||
'tabsBar/delAllRoutes'
|
||||
)
|
||||
if (this.affixtabs.some((tag) => tag.path === view.path)) {
|
||||
return
|
||||
}
|
||||
this.toLastTag(visitedRoutes, view)
|
||||
},
|
||||
toLastTag(visitedRoutes, view) {
|
||||
const latestView = visitedRoutes.slice(-1)[0]
|
||||
if (latestView) {
|
||||
this.$router.push(latestView)
|
||||
} else {
|
||||
this.$router.push('/')
|
||||
}
|
||||
},
|
||||
async toThisTag() {
|
||||
const view = this.visitedRoutes.filter((item, index) => {
|
||||
if (item.path === this.$route.fullPath) {
|
||||
return item
|
||||
}
|
||||
})[0]
|
||||
if (this.$route.path !== view.path) this.$router.push(view)
|
||||
return view
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tabs-bar-container {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: $base-tabs-bar-height;
|
||||
padding-right: $base-padding;
|
||||
padding-left: $base-padding;
|
||||
user-select: none;
|
||||
background: $base-color-white;
|
||||
border-top: 1px solid #f6f6f6;
|
||||
|
||||
::v-deep {
|
||||
.fold-unfold {
|
||||
margin-right: $base-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-content {
|
||||
width: calc(100% - 90px);
|
||||
height: $base-tag-item-height;
|
||||
|
||||
::v-deep {
|
||||
.el-tabs__nav-next,
|
||||
.el-tabs__nav-prev {
|
||||
height: $base-tag-item-height;
|
||||
line-height: $base-tag-item-height;
|
||||
}
|
||||
|
||||
.el-tabs__header {
|
||||
border-bottom: 0;
|
||||
|
||||
.el-tabs__nav {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
box-sizing: border-box;
|
||||
height: $base-tag-item-height;
|
||||
margin-right: 5px;
|
||||
line-height: $base-tag-item-height;
|
||||
border: 1px solid $base-border-color;
|
||||
border-radius: $base-border-radius;
|
||||
transition: padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
|
||||
|
||||
&.is-active {
|
||||
border: 1px solid $base-color-blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.more {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
<template>
|
||||
<div class="top-bar-container">
|
||||
<div class="vab-main">
|
||||
<el-row>
|
||||
<el-col :lg="7" :md="7" :sm="7" :xl="7" :xs="7">
|
||||
<vab-logo />
|
||||
</el-col>
|
||||
<el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="12">
|
||||
<el-menu
|
||||
:active-text-color="variables['menu-color-active']"
|
||||
:background-color="variables['menu-background']"
|
||||
:default-active="activeMenu"
|
||||
:text-color="variables['menu-color']"
|
||||
menu-trigger="hover"
|
||||
mode="horizontal"
|
||||
>
|
||||
<template v-for="route in routes">
|
||||
<vab-side-bar-item
|
||||
v-if="!route.hidden"
|
||||
:key="route.path"
|
||||
:full-path="route.path"
|
||||
:item="route"
|
||||
/>
|
||||
</template>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
<el-col :lg="5" :md="5" :sm="5" :xl="5" :xs="5">
|
||||
<div class="right-panel">
|
||||
<vab-error-log />
|
||||
<vab-full-screen-bar @refresh="refreshRoute" />
|
||||
<vab-theme-bar class="hidden-md-and-down" />
|
||||
<vab-icon
|
||||
:icon="['fas', 'redo']"
|
||||
:pulse="pulse"
|
||||
title="重载路由"
|
||||
@click="refreshRoute"
|
||||
/>
|
||||
<vab-avatar />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import variables from '@/styles/variables.scss'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'VabTopBar',
|
||||
data() {
|
||||
return {
|
||||
pulse: false,
|
||||
menuTrigger: 'hover',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
routes: 'routes/routes',
|
||||
visitedRoutes: 'tabsBar/visitedRoutes',
|
||||
}),
|
||||
activeMenu() {
|
||||
const route = this.$route
|
||||
const { meta, path } = route
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu
|
||||
}
|
||||
return path
|
||||
},
|
||||
variables() {
|
||||
return variables
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async refreshRoute() {
|
||||
this.$baseEventBus.$emit('reload-router-view')
|
||||
this.pulse = true
|
||||
setTimeout(() => {
|
||||
this.pulse = false
|
||||
}, 1000)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.top-bar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: flex-end;
|
||||
height: $base-top-bar-height;
|
||||
background: $base-menu-background;
|
||||
|
||||
.vab-main {
|
||||
background: $base-menu-background;
|
||||
|
||||
::v-deep {
|
||||
.el-menu {
|
||||
&.el-menu--horizontal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: $base-top-bar-height;
|
||||
border-bottom: 0 solid transparent !important;
|
||||
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
li:nth-child(4),
|
||||
li:nth-child(5) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
> .el-menu-item {
|
||||
height: $base-top-bar-height;
|
||||
line-height: $base-top-bar-height;
|
||||
}
|
||||
|
||||
> .el-submenu {
|
||||
.el-submenu__title {
|
||||
height: $base-top-bar-height;
|
||||
line-height: $base-top-bar-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
&--horizontal {
|
||||
.el-menu {
|
||||
.el-menu-item,
|
||||
.el-submenu__title {
|
||||
height: $base-menu-item-height;
|
||||
line-height: $base-menu-item-height;
|
||||
}
|
||||
}
|
||||
|
||||
.el-submenu,
|
||||
.el-menu-item {
|
||||
&.is-active {
|
||||
background-color: $base-color-blue !important;
|
||||
border-bottom: 0 solid transparent !important;
|
||||
|
||||
.el-submenu__title {
|
||||
border-bottom: 0 solid transparent !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .el-menu-item {
|
||||
.el-tag {
|
||||
margin-top: calc(#{$base-top-bar-height} / 2 - 7.5px);
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1199px) {
|
||||
.el-tag {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: transparent !important;
|
||||
border-bottom: 3px solid $base-color-blue !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: $base-top-bar-height;
|
||||
|
||||
::v-deep {
|
||||
.user-name {
|
||||
color: rgba($base-color-white, 0.9);
|
||||
}
|
||||
|
||||
.user-name + i {
|
||||
color: rgba($base-color-white, 0.9);
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: 15px;
|
||||
font-size: $base-font-size-big;
|
||||
color: rgba($base-color-white, 0.9);
|
||||
cursor: pointer;
|
||||
fill: rgba($base-color-white, 0.9);
|
||||
}
|
||||
|
||||
button {
|
||||
svg {
|
||||
margin-right: 0;
|
||||
color: rgba($base-color-white, 0.9);
|
||||
cursor: pointer;
|
||||
fill: rgba($base-color-white, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.el-badge {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
module.exports = {
|
||||
webpackBarName: 'vue-admin-better',
|
||||
webpackBanner:
|
||||
' build: vue-admin-better \n vue-admin-beautiful.com \n https://gitee.com/chu1204505056/vue-admin-better \n time: ',
|
||||
donationConsole() {
|
||||
const chalk = require('chalk')
|
||||
console.log(
|
||||
chalk.green(
|
||||
`> 欢迎使用vue-admin-better,github开源地址:https://github.com/chuzhixin/vue-admin-better`
|
||||
)
|
||||
)
|
||||
console.log(
|
||||
chalk.green(
|
||||
`> 欢迎使用vue-admin-better,码云开源地址:https://gitee.com/chu1204505056/vue-admin-better`
|
||||
)
|
||||
)
|
||||
|
||||
console.log(
|
||||
chalk.green(`> pro版演示地址:http://vue-admin-beautiful.com/admin-pro`)
|
||||
)
|
||||
|
||||
console.log(
|
||||
chalk.green(`> plus版演示地址:http://vue-admin-beautiful.com/admin-plus`)
|
||||
)
|
||||
|
||||
console.log(
|
||||
chalk.green(`> shop版演示地址:http://vue-admin-beautiful.com/shop-vite`)
|
||||
)
|
||||
|
||||
console.log(
|
||||
chalk.green(
|
||||
`> 使用中出现任何问题可加QQ群反馈,获取基础版、文档,请我们喝杯咖啡(如若情况不允许,请勿勉强):https://gitee.com/chu1204505056/vue-admin-better#-%E5%89%8D%E7%AB%AF%E8%AE%A8%E8%AE%BA-qq-%E7%BE%A4`
|
||||
)
|
||||
)
|
||||
|
||||
console.log(chalk.green(`> 如果您不希望显示以上信息,可在config中配置关闭`))
|
||||
console.log('\n')
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "layouts",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
module.exports = {
|
||||
printWidth: 80,
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
quoteProps: 'as-needed',
|
||||
jsxSingleQuote: false,
|
||||
trailingComma: 'es5',
|
||||
bracketSpacing: true,
|
||||
jsxBracketSameLine: false,
|
||||
arrowParens: 'always',
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
vueIndentScriptAndStyle: true,
|
||||
endOfLine: 'lf',
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
const data = [
|
||||
'alphabetical_sorting',
|
||||
'alphabetical_sorting',
|
||||
'alarm_clock',
|
||||
'area_chart',
|
||||
'approval',
|
||||
'answers',
|
||||
'approve',
|
||||
'assistant',
|
||||
'automotive',
|
||||
'automatic',
|
||||
'bad_decision',
|
||||
'bar_chart',
|
||||
'bearish',
|
||||
'biomass',
|
||||
'biohazard',
|
||||
'binoculars',
|
||||
'bookmark',
|
||||
'briefcase',
|
||||
'biotech',
|
||||
'broken_link',
|
||||
'business',
|
||||
'bullish',
|
||||
'business_contact',
|
||||
'businesswoman',
|
||||
'cable_release',
|
||||
'calculator',
|
||||
'businessman',
|
||||
'calendar',
|
||||
'butting_in',
|
||||
'call_transfer',
|
||||
'callback',
|
||||
'camcorder',
|
||||
'camera',
|
||||
'camcorder_pro',
|
||||
'cancel',
|
||||
'camera_addon',
|
||||
'camera_identificatio',
|
||||
'capacitor',
|
||||
'candle_sticks',
|
||||
'checkmark',
|
||||
'circuit',
|
||||
'charge_battery',
|
||||
'clear_filters',
|
||||
'clapperboard',
|
||||
'clock',
|
||||
'close_up_mode',
|
||||
'collaboration',
|
||||
'cell_phone',
|
||||
'collapse',
|
||||
'collect',
|
||||
'cloth',
|
||||
'combo_chart',
|
||||
'comments',
|
||||
'conference_call',
|
||||
'compact_camera',
|
||||
'contacts',
|
||||
'copyleft',
|
||||
'copyright',
|
||||
'crystal_oscillator',
|
||||
'cursor',
|
||||
'currency_exchange',
|
||||
'customer_support',
|
||||
'dam',
|
||||
'data_backup',
|
||||
'data_configuration',
|
||||
'data_encryption',
|
||||
'data_protection',
|
||||
'data_recovery',
|
||||
'database',
|
||||
'data_sheet',
|
||||
'debt',
|
||||
'decision',
|
||||
'delete_column',
|
||||
'delete_database',
|
||||
'department',
|
||||
'delete_row',
|
||||
'deployment',
|
||||
'dislike',
|
||||
'disapprove',
|
||||
'disclaimer',
|
||||
'display',
|
||||
'document',
|
||||
'do_not_insert',
|
||||
'do_not_mix',
|
||||
'do_not_inhale',
|
||||
'donate',
|
||||
'down',
|
||||
'doughnut_chart',
|
||||
'down_left',
|
||||
'down_right',
|
||||
'download',
|
||||
'edit_image',
|
||||
'electrical_sensor',
|
||||
'electrical_threshold',
|
||||
'electricity',
|
||||
'electro_devices',
|
||||
'electronics',
|
||||
'empty_battery',
|
||||
'empty_filter',
|
||||
'empty_trash',
|
||||
'end_call',
|
||||
'engineering',
|
||||
'entering_heaven_aliv',
|
||||
'expand',
|
||||
'export',
|
||||
'expired',
|
||||
'factory',
|
||||
'factory_breakdown',
|
||||
'external',
|
||||
'faq',
|
||||
'feed_in',
|
||||
'file',
|
||||
'feedback',
|
||||
'film',
|
||||
'filled_filter',
|
||||
'filing_cabinet',
|
||||
'film_reel',
|
||||
'flash_auto',
|
||||
'fine_print',
|
||||
'flash_off',
|
||||
'flash_on',
|
||||
'flow_chart',
|
||||
'folder',
|
||||
'frame',
|
||||
'full_battery',
|
||||
'full_trash',
|
||||
'gallery',
|
||||
'generic_sorting_asc',
|
||||
'generic_sorting_desc',
|
||||
'genealogy',
|
||||
'globe',
|
||||
'good_decision',
|
||||
'headset',
|
||||
'grid',
|
||||
'graduation_cap',
|
||||
'heat_map',
|
||||
'high_priority',
|
||||
'high_battery',
|
||||
'image_file',
|
||||
'home',
|
||||
'idea',
|
||||
'import',
|
||||
'in_transit',
|
||||
'integrated_webcam',
|
||||
'inspection',
|
||||
'invite',
|
||||
'internal',
|
||||
'ipad',
|
||||
'info',
|
||||
'iphone',
|
||||
'kindle',
|
||||
'key',
|
||||
'landscape',
|
||||
'left',
|
||||
'left_down',
|
||||
'left_up',
|
||||
'leave',
|
||||
'like_placeholder',
|
||||
'light_at_the_end_of_',
|
||||
'library',
|
||||
'line_chart',
|
||||
'link',
|
||||
'like',
|
||||
'lock',
|
||||
'list',
|
||||
'lock_landscape',
|
||||
'low_battery',
|
||||
'lock_portrait',
|
||||
'low_priority',
|
||||
'make_decision',
|
||||
'medium_priority',
|
||||
'manager',
|
||||
'menu',
|
||||
'middle_battery',
|
||||
'minus',
|
||||
'missed_call',
|
||||
'mind_map',
|
||||
'mms',
|
||||
'multiple_cameras',
|
||||
'money_transfer',
|
||||
'music',
|
||||
'multiple_devices',
|
||||
'multiple_smartphones',
|
||||
'multiple_inputs',
|
||||
'negative_dynamic',
|
||||
'neutral_decision',
|
||||
'night_landscape',
|
||||
'news',
|
||||
'neutral_trading',
|
||||
'night_portrait',
|
||||
'no_idea',
|
||||
'next',
|
||||
'no_video',
|
||||
'nook',
|
||||
'ok',
|
||||
'org_unit',
|
||||
'opened_folder',
|
||||
'old_time_camera',
|
||||
'online_support',
|
||||
'organization',
|
||||
'package',
|
||||
'paid',
|
||||
'parallel_tasks',
|
||||
'overtime',
|
||||
'panorama',
|
||||
'phone',
|
||||
'phone_android',
|
||||
'photo_reel',
|
||||
'pie_chart',
|
||||
'picture',
|
||||
'planner',
|
||||
'plus',
|
||||
'podium_with_audience',
|
||||
'podium_without_speak',
|
||||
'podium_with_speaker',
|
||||
'previous',
|
||||
'portrait_mode',
|
||||
'positive_dynamic',
|
||||
'privacy',
|
||||
'process',
|
||||
'puzzle',
|
||||
'questions',
|
||||
'print',
|
||||
'radar_plot',
|
||||
'rating',
|
||||
'ratings',
|
||||
'reading',
|
||||
'redo',
|
||||
'reading_ebook',
|
||||
'refresh',
|
||||
'registered_trademark',
|
||||
'right',
|
||||
'reuse',
|
||||
'remove_image',
|
||||
'right_down',
|
||||
'right_up',
|
||||
'rotate_to_portrait',
|
||||
'rules',
|
||||
'rotate_camera',
|
||||
'rotate_to_landscape',
|
||||
'ruler',
|
||||
'scatter_plot',
|
||||
'search',
|
||||
'safe',
|
||||
'self_service_kiosk',
|
||||
'selfie',
|
||||
'serial_tasks',
|
||||
'sales_performance',
|
||||
'settings',
|
||||
'services',
|
||||
'share',
|
||||
'shipped',
|
||||
'sim_card',
|
||||
'shop',
|
||||
'service_mark',
|
||||
'sim_card_chip',
|
||||
'signature',
|
||||
'smartphone_tablet',
|
||||
'sound_recording_copy',
|
||||
'sms',
|
||||
'speaker',
|
||||
'slr_back_side',
|
||||
'start',
|
||||
'stack_of_photos',
|
||||
'statistics',
|
||||
'sports_mode',
|
||||
'support',
|
||||
'synchronize',
|
||||
'switch_camera',
|
||||
'survey',
|
||||
'tablet_android',
|
||||
'template',
|
||||
'trademark',
|
||||
'todo_list',
|
||||
'touchscreen_smartpho',
|
||||
'timeline',
|
||||
'tree_structure',
|
||||
'undo',
|
||||
'up_left',
|
||||
'two_smartphones',
|
||||
'unlock',
|
||||
'up',
|
||||
'up_right',
|
||||
'upload',
|
||||
'video_call',
|
||||
'video_file',
|
||||
'view_details',
|
||||
'video_projector',
|
||||
'vip',
|
||||
'voice_presentation',
|
||||
'webcam',
|
||||
'voicemail',
|
||||
'workflow',
|
||||
'about',
|
||||
'accept_database',
|
||||
'add_image',
|
||||
'add_column',
|
||||
'add_database',
|
||||
'add_row',
|
||||
]
|
||||
module.exports = [
|
||||
{
|
||||
url: '/colorfulIcon/getList',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
const { title, pageNo = 1, pageSize = 72 } = config.body
|
||||
let mockList = data.filter((item) => {
|
||||
if (title && item.indexOf(title) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageNo && index >= pageSize * (pageNo - 1))
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount: mockList.length,
|
||||
data: pageList,
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
const { mock } = require('mockjs')
|
||||
|
||||
const List = []
|
||||
const count = 999
|
||||
let num = 0
|
||||
for (let i = 0; i < count; i++) {
|
||||
List.push(
|
||||
mock({
|
||||
uuid: '@uuid',
|
||||
image: `https://picsum.photos/300/600?random=${num++}`,
|
||||
title: '@ctitle',
|
||||
description: '@csentence',
|
||||
link: 'https://www.baidu.com',
|
||||
price: '@integer(100, 500)',
|
||||
'status|1': [1, 0],
|
||||
'isRecommend|1': [1, 0],
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
url: '/goodsList/getList',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
const { title = '', pageNo = 1, pageSize = 20 } = config.body
|
||||
let mockList = List.filter((item) => {
|
||||
if (title && item.title.indexOf(title) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageNo && index >= pageSize * (pageNo - 1))
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount: count,
|
||||
data: pageList,
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
module.exports = [
|
||||
{
|
||||
url: '/menuManagement/getTree',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount: 999,
|
||||
data: [
|
||||
{
|
||||
id: 'root',
|
||||
label: '全部角色',
|
||||
children: [
|
||||
{
|
||||
id: '@id',
|
||||
permission: 'admin',
|
||||
label: 'admin角色',
|
||||
},
|
||||
{
|
||||
id: '@id',
|
||||
permission: 'editor',
|
||||
label: 'editor角色',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/menuManagement/doEdit',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟保存成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/menuManagement/doDelete',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟删除成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
const { mock } = require('mockjs')
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
url: '/personalCenter/getList',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount: 999,
|
||||
data: mock({
|
||||
'data|10': [
|
||||
{
|
||||
id: '@id',
|
||||
},
|
||||
],
|
||||
}).data,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/personalCenter/doEdit',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟保存成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/personalCenter/doDelete',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟删除成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
const totalCount = 2
|
||||
const List = [
|
||||
{
|
||||
id: '@id',
|
||||
permission: 'admin',
|
||||
},
|
||||
{
|
||||
id: '@id',
|
||||
permission: 'editor',
|
||||
},
|
||||
]
|
||||
module.exports = [
|
||||
{
|
||||
url: '/roleManagement/getList',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
const { title = '', pageNo = 1, pageSize = 20 } = config.body
|
||||
let mockList = List.filter((item) => {
|
||||
return !(title && item.title.indexOf(title) < 0)
|
||||
})
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageNo && index >= pageSize * (pageNo - 1))
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount,
|
||||
data: pageList,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/roleManagement/doEdit',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟保存成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/roleManagement/doDelete',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟删除成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -2,36 +2,267 @@ const data = [
|
|||
{
|
||||
path: '/',
|
||||
component: 'Layout',
|
||||
redirect: '/index',
|
||||
meta: {
|
||||
title: '首页',
|
||||
icon: 'home-4-line',
|
||||
affix: true,
|
||||
},
|
||||
redirect: 'index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: 'Index',
|
||||
component: '@/views/index',
|
||||
component: '@/views/index/index',
|
||||
meta: {
|
||||
title: '首页',
|
||||
icon: 'home-4-line',
|
||||
icon: 'home',
|
||||
affix: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/personnelManagement',
|
||||
component: 'Layout',
|
||||
redirect: 'noRedirect',
|
||||
name: 'PersonnelManagement',
|
||||
meta: { title: '人员', icon: 'users-cog', permissions: ['admin'] },
|
||||
children: [
|
||||
{
|
||||
path: 'userManagement',
|
||||
name: 'UserManagement',
|
||||
component: '@/views/personnelManagement/userManagement/index',
|
||||
meta: { title: '用户管理' },
|
||||
},
|
||||
{
|
||||
path: 'roleManagement',
|
||||
name: 'RoleManagement',
|
||||
component: '@/views/personnelManagement/roleManagement/index',
|
||||
meta: { title: '角色管理' },
|
||||
},
|
||||
{
|
||||
path: 'menuManagement',
|
||||
name: 'MenuManagement',
|
||||
component: '@/views/personnelManagement/menuManagement/index',
|
||||
meta: { title: '菜单管理', badge: 'New' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/vab',
|
||||
component: 'Layout',
|
||||
redirect: 'noRedirect',
|
||||
name: 'Vab',
|
||||
alwaysShow: true,
|
||||
meta: { title: '组件', icon: 'cloud' },
|
||||
children: [
|
||||
{
|
||||
path: 'permissions',
|
||||
name: 'Permission',
|
||||
component: '@/views/vab/permissions/index',
|
||||
meta: {
|
||||
title: '权限控制',
|
||||
permissions: ['admin', 'editor'],
|
||||
badge: 'New',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'icon',
|
||||
component: 'EmptyLayout',
|
||||
redirect: 'noRedirect',
|
||||
name: 'Icon',
|
||||
meta: {
|
||||
title: '图标',
|
||||
permissions: ['admin'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'awesomeIcon',
|
||||
name: 'AwesomeIcon',
|
||||
component: '@/views/vab/icon/index',
|
||||
meta: { title: '常规图标' },
|
||||
},
|
||||
{
|
||||
path: 'colorfulIcon',
|
||||
name: 'ColorfulIcon',
|
||||
component: '@/views/vab/icon/colorfulIcon',
|
||||
meta: { title: '多彩图标' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'table',
|
||||
component: '@/views/vab/table/index',
|
||||
name: 'Table',
|
||||
meta: {
|
||||
title: '表格',
|
||||
permissions: ['admin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'map',
|
||||
name: 'Map',
|
||||
component: '@/views/vab/map/index',
|
||||
meta: { title: '地图', permissions: ['admin'], badge: 'Pro' },
|
||||
},
|
||||
{
|
||||
path: 'webSocket',
|
||||
name: 'WebSocket',
|
||||
component: '@/views/vab/webSocket/index',
|
||||
meta: { title: 'webSocket', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'form',
|
||||
name: 'Form',
|
||||
component: '@/views/vab/form/index',
|
||||
meta: { title: '表单', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'element',
|
||||
name: 'Element',
|
||||
component: '@/views/vab/element/index',
|
||||
meta: { title: '常用组件', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'tree',
|
||||
name: 'Tree',
|
||||
component: '@/views/vab/tree/index',
|
||||
meta: { title: '树', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'verify',
|
||||
name: 'Verify',
|
||||
component: '@/views/vab/verify/index',
|
||||
meta: { title: '验证码', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'menu1',
|
||||
component: '@/views/vab/nested/menu1/index',
|
||||
name: 'Menu1',
|
||||
alwaysShow: true,
|
||||
meta: {
|
||||
title: '嵌套路由 1',
|
||||
permissions: ['admin'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1',
|
||||
name: 'Menu1-1',
|
||||
alwaysShow: true,
|
||||
meta: { title: '嵌套路由 1-1' },
|
||||
component: '@/views/vab/nested/menu1/menu1-1/index',
|
||||
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1-1',
|
||||
name: 'Menu1-1-1',
|
||||
meta: { title: '嵌套路由 1-1-1' },
|
||||
component: '@/views/vab/nested/menu1/menu1-1/menu1-1-1/index',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'loading',
|
||||
name: 'Loading',
|
||||
component: '@/views/vab/loading/index',
|
||||
meta: { title: 'loading', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'backToTop',
|
||||
name: 'BackToTop',
|
||||
component: '@/views/vab/backToTop/index',
|
||||
meta: { title: '返回顶部', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'lodash',
|
||||
name: 'Lodash',
|
||||
component: '@/views/vab/lodash/index',
|
||||
meta: { title: 'lodash', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'smallComponents',
|
||||
name: 'SmallComponents',
|
||||
component: '@/views/vab/smallComponents/index',
|
||||
meta: { title: '小组件', permissions: ['admin'] },
|
||||
},
|
||||
|
||||
{
|
||||
path: 'upload',
|
||||
name: 'Upload',
|
||||
component: '@/views/vab/upload/index',
|
||||
meta: { title: '上传', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'log',
|
||||
name: 'Log',
|
||||
component: '@/views/vab/errorLog/index',
|
||||
meta: { title: '错误日志模拟', permissions: ['admin'] },
|
||||
},
|
||||
{
|
||||
path: 'more',
|
||||
name: 'More',
|
||||
component: '@/views/vab/more/index',
|
||||
meta: { title: '关于', permissions: ['admin'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/mall',
|
||||
component: 'Layout',
|
||||
redirect: 'noRedirect',
|
||||
name: 'Mall',
|
||||
meta: {
|
||||
title: '商城',
|
||||
icon: 'shopping-cart',
|
||||
permissions: ['admin'],
|
||||
},
|
||||
|
||||
children: [
|
||||
{
|
||||
path: 'pay',
|
||||
name: 'Pay',
|
||||
component: '@/views/mall/pay/index',
|
||||
meta: {
|
||||
title: '支付',
|
||||
noKeepAlive: true,
|
||||
},
|
||||
children: null,
|
||||
},
|
||||
{
|
||||
path: 'goodsList',
|
||||
name: 'GoodsList',
|
||||
component: '@/views/mall/goodsList/index',
|
||||
meta: {
|
||||
title: '商品列表',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/error',
|
||||
component: 'EmptyLayout',
|
||||
redirect: 'noRedirect',
|
||||
name: 'Error',
|
||||
meta: { title: '错误页', icon: 'bug' },
|
||||
children: [
|
||||
{
|
||||
path: '401',
|
||||
name: 'Error401',
|
||||
component: '@/views/401',
|
||||
meta: { title: '401' },
|
||||
},
|
||||
{
|
||||
path: '404',
|
||||
name: 'Error404',
|
||||
component: '@/views/404',
|
||||
meta: { title: '404' },
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
module.exports = [
|
||||
{
|
||||
url: '/menu/navigate',
|
||||
type: 'get',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
data,
|
||||
}
|
||||
return { code: 200, msg: 'success', data: data }
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
const { mock } = require('mockjs')
|
||||
const { handleRandomImage } = require('../utils')
|
||||
|
||||
const List = []
|
||||
const count = 50
|
||||
const count = 999
|
||||
for (let i = 0; i < count; i++) {
|
||||
List.push(
|
||||
mock({
|
||||
uuid: '@uuid',
|
||||
id: '@id',
|
||||
title: '@title(1, 2)',
|
||||
description: '@csentence',
|
||||
title: '@csentence(1, 2)',
|
||||
'status|1': ['published', 'draft', 'deleted'],
|
||||
author: '@cname',
|
||||
datetime: '@datetime',
|
||||
pageViews: '@integer(300, 5000)',
|
||||
img: handleRandomImage(228, 228),
|
||||
img: handleRandomImage(200, 200),
|
||||
smallImg: handleRandomImage(40, 40),
|
||||
switch: '@boolean',
|
||||
percent: '@integer(80,99)',
|
||||
'rate|1': [1, 2, 3, 4, 5],
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
@ -24,20 +24,40 @@ for (let i = 0; i < count; i++) {
|
|||
module.exports = [
|
||||
{
|
||||
url: '/table/getList',
|
||||
type: 'get',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
const { title, current = 1, pageSize = 10 } = config.query
|
||||
if (!config.body) {
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount: count,
|
||||
data: mock({
|
||||
'data|50': [
|
||||
{
|
||||
id: '@id',
|
||||
title: '@csentence(1, 2)',
|
||||
'status|1': ['published', 'draft', 'deleted'],
|
||||
author: '@cname',
|
||||
datetime: '@datetime',
|
||||
pageViews: '@integer(300, 5000)',
|
||||
img: handleRandomImage(200, 200),
|
||||
smallImg: handleRandomImage(40, 40),
|
||||
switch: '@boolean',
|
||||
percent: '@integer(80,99)',
|
||||
},
|
||||
],
|
||||
}).data,
|
||||
}
|
||||
}
|
||||
const { title = '', pageNo = 1, pageSize = 20 } = config.body
|
||||
let mockList = List.filter((item) => {
|
||||
return !(title && item.title.indexOf(title) < 0)
|
||||
})
|
||||
const pageList = mockList.filter(
|
||||
(item, index) =>
|
||||
index < pageSize * current && index >= pageSize * (current - 1)
|
||||
)
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageNo && index >= pageSize * (pageNo - 1))
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
total: mockList.length,
|
||||
totalCount: count,
|
||||
data: pageList,
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
const data = [
|
||||
{
|
||||
id: '1',
|
||||
parentId: '0',
|
||||
name: 'root',
|
||||
title: 'root',
|
||||
text: 'root',
|
||||
value: '1',
|
||||
rank: 1,
|
||||
children: [
|
||||
{
|
||||
id: '32816b88ff72423f960e7d492a386131',
|
||||
parentId: '1',
|
||||
name: '一级',
|
||||
title: '一级',
|
||||
text: '一级',
|
||||
value: '32816b88ff72423f960e7d492a386131',
|
||||
rank: 2,
|
||||
children: [
|
||||
{
|
||||
id: '9e11afc35d55475fb0bd3164b9684cbe',
|
||||
parentId: '32816b88ff72423f960e7d492a386131',
|
||||
name: '二级',
|
||||
title: '二级',
|
||||
text: '二级',
|
||||
value: '9e11afc35d55475fb0bd3164b9684cbe',
|
||||
rank: 3,
|
||||
children: [
|
||||
{
|
||||
id: '4cc1b04635e4444292526c5391699077',
|
||||
parentId: '9e11afc35d55475fb0bd3164b9684cbe',
|
||||
name: '三级',
|
||||
title: '三级',
|
||||
text: '三级',
|
||||
value: '4cc1b04635e4444292526c5391699077',
|
||||
rank: 4,
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
module.exports = [
|
||||
{
|
||||
url: '/tree/list',
|
||||
type: 'post',
|
||||
response() {
|
||||
return { code: 200, msg: 'success', data }
|
||||
},
|
||||
},
|
||||
]
|
||||
|
|
@ -5,6 +5,21 @@ const accessTokens = {
|
|||
}
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
url: '/publicKey',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
data: {
|
||||
mockServer: true,
|
||||
publicKey:
|
||||
'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBT2vr+dhZElF73FJ6xiP181txKWUSNLPQQlid6DUJhGAOZblluafIdLmnUyKE8mMHhT3R+Ib3ssZcJku6Hn72yHYj/qPkCGFv0eFo7G+GJfDIUeDyalBN0QsuiE/XzPHJBuJDfRArOiWvH0BXOv5kpeXSXM8yTt5Na1jAYSiQ/wIDAQAB',
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/login',
|
||||
type: 'post',
|
||||
|
|
@ -24,24 +39,6 @@ module.exports = [
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/socialLogin',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
const { code } = config.body
|
||||
if (!code) {
|
||||
return {
|
||||
code: 500,
|
||||
msg: '未成功获取Token。',
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
data: { accessToken: accessTokens['admin'] },
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/register',
|
||||
type: 'post',
|
||||
|
|
@ -57,35 +54,27 @@ module.exports = [
|
|||
type: 'post',
|
||||
response(config) {
|
||||
const { accessToken } = config.body
|
||||
let roles = ['admin']
|
||||
let ability = ['READ']
|
||||
let permissions = ['admin']
|
||||
let username = 'admin'
|
||||
if ('admin-accessToken' === accessToken) {
|
||||
roles = ['admin']
|
||||
ability = ['READ', 'WRITE', 'DELETE']
|
||||
permissions = ['admin']
|
||||
username = 'admin'
|
||||
}
|
||||
if ('editor-accessToken' === accessToken) {
|
||||
roles = ['editor']
|
||||
ability = ['READ', 'WRITE']
|
||||
permissions = ['editor']
|
||||
username = 'editor'
|
||||
}
|
||||
if ('test-accessToken' === accessToken) {
|
||||
roles = ['admin', 'editor']
|
||||
ability = ['READ']
|
||||
permissions = ['admin', 'editor']
|
||||
username = 'test'
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
data: {
|
||||
roles,
|
||||
ability,
|
||||
permissions,
|
||||
username,
|
||||
'avatar|1': [
|
||||
'https://i.gtimg.cn/club/item/face/img/2/15922_100.gif',
|
||||
'https://i.gtimg.cn/club/item/face/img/8/15918_100.gif',
|
||||
],
|
||||
'avatar|1': ['https://i.gtimg.cn/club/item/face/img/2/15922_100.gif', 'https://i.gtimg.cn/club/item/face/img/8/15918_100.gif'],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
const totalCount = 3
|
||||
const List = [
|
||||
{
|
||||
id: '@id',
|
||||
username: 'admin',
|
||||
password: 'admin',
|
||||
email: '@email',
|
||||
permissions: ['admin'],
|
||||
datatime: '@datetime',
|
||||
},
|
||||
{
|
||||
id: '@id',
|
||||
username: 'editor',
|
||||
password: 'editor',
|
||||
email: '@email',
|
||||
permissions: ['editor'],
|
||||
datatime: '@datetime',
|
||||
},
|
||||
{
|
||||
id: '@id',
|
||||
username: 'test',
|
||||
password: 'test',
|
||||
email: '@email',
|
||||
permissions: ['admin', 'editor'],
|
||||
datatime: '@datetime',
|
||||
},
|
||||
]
|
||||
module.exports = [
|
||||
{
|
||||
url: '/userManagement/getList',
|
||||
type: 'post',
|
||||
response(config) {
|
||||
const { title = '', pageNo = 1, pageSize = 20 } = config.body
|
||||
let mockList = List.filter((item) => {
|
||||
if (title && item.title.indexOf(title) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageNo && index >= pageSize * (pageNo - 1))
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'success',
|
||||
totalCount,
|
||||
data: pageList,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/userManagement/doEdit',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟保存成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/userManagement/doDelete',
|
||||
type: 'post',
|
||||
response() {
|
||||
return {
|
||||
code: 200,
|
||||
msg: '模拟删除成功',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
108
mock/index.js
|
|
@ -1,16 +1,98 @@
|
|||
/**
|
||||
* @author chuzhixin 1204505056@qq.com
|
||||
* @description 导入所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。
|
||||
*/
|
||||
|
||||
const chokidar = require('chokidar')
|
||||
const bodyParser = require('body-parser')
|
||||
const chalk = require('chalk')
|
||||
const path = require('path')
|
||||
const { mock } = require('mockjs')
|
||||
const { baseURL } = require('../src/config')
|
||||
const mockDir = path.join(process.cwd(), 'mock')
|
||||
const { handleMockArray } = require('./utils')
|
||||
|
||||
const mocks = []
|
||||
const mockArray = handleMockArray()
|
||||
mockArray.forEach((item) => {
|
||||
const obj = require(item)
|
||||
mocks.push(...obj)
|
||||
})
|
||||
module.exports = {
|
||||
mocks,
|
||||
/**
|
||||
*
|
||||
* @param app
|
||||
* @returns {{mockStartIndex: number, mockRoutesLength: number}}
|
||||
*/
|
||||
const registerRoutes = (app) => {
|
||||
let mockLastIndex
|
||||
const mocks = []
|
||||
const mockArray = handleMockArray()
|
||||
mockArray.forEach((item) => {
|
||||
const obj = require(item)
|
||||
mocks.push(...obj)
|
||||
})
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url
|
||||
* @param type
|
||||
* @param respond
|
||||
* @returns {{response(*=, *=): void, type: (*|string), url: RegExp}}
|
||||
*/
|
||||
const responseFake = (url, type, respond) => {
|
||||
return {
|
||||
url: new RegExp(`${baseURL}${url}`),
|
||||
type: type || 'get',
|
||||
response(req, res) {
|
||||
res.status(200)
|
||||
if (JSON.stringify(req.body) !== '{}') {
|
||||
console.log(chalk.green(`> 请求地址:${req.path}`))
|
||||
console.log(chalk.green(`> 请求参数:${JSON.stringify(req.body)}\n`))
|
||||
} else {
|
||||
console.log(chalk.green(`> 请求地址:${req.path}\n`))
|
||||
}
|
||||
res.json(mock(respond instanceof Function ? respond(req, res) : respond))
|
||||
},
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param app
|
||||
*/
|
||||
module.exports = (app) => {
|
||||
app.use(bodyParser.json())
|
||||
app.use(
|
||||
bodyParser.urlencoded({
|
||||
extended: true,
|
||||
})
|
||||
)
|
||||
|
||||
const mockRoutes = registerRoutes(app)
|
||||
let mockRoutesLength = mockRoutes.mockRoutesLength
|
||||
let mockStartIndex = mockRoutes.mockStartIndex
|
||||
chokidar
|
||||
.watch(mockDir, {
|
||||
ignored: /mock-server/,
|
||||
ignoreInitial: true,
|
||||
})
|
||||
.on('all', (event) => {
|
||||
if (event === 'change' || event === 'add') {
|
||||
try {
|
||||
app._router.stack.splice(mockStartIndex, mockRoutesLength)
|
||||
|
||||
Object.keys(require.cache).forEach((item) => {
|
||||
if (item.includes(mockDir)) {
|
||||
delete require.cache[require.resolve(item)]
|
||||
}
|
||||
})
|
||||
const mockRoutes = registerRoutes(app)
|
||||
mockRoutesLength = mockRoutes.mockRoutesLength
|
||||
mockStartIndex = mockRoutes.mockStartIndex
|
||||
} catch (error) {
|
||||
console.log(chalk.red(error))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,94 +0,0 @@
|
|||
const chokidar = require('chokidar')
|
||||
const bodyParser = require('body-parser')
|
||||
const chalk = require('chalk')
|
||||
const path = require('path')
|
||||
const Mock = require('mockjs')
|
||||
const { baseURL } = require('../src/config')
|
||||
const mockDir = path.join(process.cwd(), 'mock')
|
||||
|
||||
/**
|
||||
*
|
||||
* @param app
|
||||
* @returns {{mockStartIndex: number, mockRoutesLength: number}}
|
||||
*/
|
||||
const 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,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param url
|
||||
* @param type
|
||||
* @param respond
|
||||
* @returns {{response(*=, *=): void, type: (*|string), url: RegExp}}
|
||||
*/
|
||||
const responseFake = (url, type, respond) => {
|
||||
return {
|
||||
url: new RegExp(`${baseURL}${url}`),
|
||||
type: type || 'get',
|
||||
response(req, res) {
|
||||
res.status(200)
|
||||
if (JSON.stringify(req.body) !== '{}') {
|
||||
console.log(chalk.green(`> 请求地址:${req.path}`))
|
||||
console.log(chalk.green(`> 请求参数:${JSON.stringify(req.body)}\n`))
|
||||
} else {
|
||||
console.log(chalk.green(`> 请求地址:${req.path}\n`))
|
||||
}
|
||||
res.json(
|
||||
Mock.mock(respond instanceof Function ? respond(req, res) : respond)
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param app
|
||||
*/
|
||||
module.exports = (app) => {
|
||||
app.use(bodyParser.json())
|
||||
app.use(
|
||||
bodyParser.urlencoded({
|
||||
extended: true,
|
||||
})
|
||||
)
|
||||
|
||||
const mockRoutes = registerRoutes(app)
|
||||
let mockRoutesLength = mockRoutes.mockRoutesLength
|
||||
let mockStartIndex = mockRoutes.mockStartIndex
|
||||
chokidar
|
||||
.watch(mockDir, {
|
||||
ignored: /mock-server/,
|
||||
ignoreInitial: true,
|
||||
})
|
||||
.on('all', (event) => {
|
||||
if (event === 'change' || event === 'add') {
|
||||
try {
|
||||
app._router.stack.splice(mockStartIndex, mockRoutesLength)
|
||||
|
||||
Object.keys(require.cache).forEach((item) => {
|
||||
if (item.includes(mockDir)) {
|
||||
delete require.cache[require.resolve(item)]
|
||||
}
|
||||
})
|
||||
const mockRoutes = registerRoutes(app)
|
||||
mockRoutesLength = mockRoutes.mockRoutesLength
|
||||
mockStartIndex = mockRoutes.mockStartIndex
|
||||
} catch (error) {
|
||||
console.log(chalk.red(error))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ const { join } = require('path')
|
|||
const fs = require('fs')
|
||||
|
||||
/**
|
||||
* @author chuzhixin 1204505056@qq.com
|
||||
* @author https://vue-admin-beautiful.com (不想保留author可删除)
|
||||
* @description 随机生成图片url。
|
||||
* @param width
|
||||
* @param height
|
||||
|
|
@ -14,7 +14,7 @@ function handleRandomImage(width = 50, height = 50) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @author chuzhixin 1204505056@qq.com
|
||||
* @author https://vue-admin-beautiful.com (不想保留author可删除)
|
||||
* @description 处理所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。
|
||||
* @returns {[]}
|
||||
*/
|
||||
|
|
@ -37,6 +37,7 @@ function handleMockArray() {
|
|||
getFiles('mock/controller')
|
||||
return mockArray
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
handleRandomImage,
|
||||
handleMockArray,
|
||||
|
|
|
|||
132
package.json
|
|
@ -1,56 +1,22 @@
|
|||
{
|
||||
"name": "vue-admin-beautiful-antdv",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"name": "vue-admin-better",
|
||||
"version": "2.5.0",
|
||||
"author": "vue-admin-better",
|
||||
"participants": [],
|
||||
"homepage": "https://chu1204505056.gitee.io/vue-admin-better",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"clear": "rimraf node_modules&&npm install --registry=https://registry.npm.taobao.org",
|
||||
"use:npm": "nrm use npm",
|
||||
"use:taobao": "nrm use taobao",
|
||||
"update": "ncu -u --target greatest&&npm install --registry=https://registry.npm.taobao.org",
|
||||
"deploy": "start ./deploy.sh"
|
||||
"serve": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
|
||||
"build": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build",
|
||||
"lint": "vue-cli-service lint --fix",
|
||||
"lint:eslint": "eslint {src,mock,library}/**/*.{vue,js,ts} --fix",
|
||||
"lint:prettier": "prettier {src,mock,library}/**/*.{html,vue,css,sass,scss,js,ts,md} --write",
|
||||
"clear": "rimraf node_modules&&npm install --registry=--registry=https://registry.npmmirror.com",
|
||||
"update": "ncu -u --reject webpack,eslint-plugin-prettier,@vue/eslint-config-prettier,prettier,layouts,sass-loader,sass,screenfull,eslint,chalk,vue,vue-template-compiler,vue-router,vuex,@vue/cli-plugin-babel,@vue/cli-plugin-eslint,@vue/cli-service,eslint-plugin-vue --registry=https://registry.npmmirror.com&&pnpm i",
|
||||
"push": "start ./push.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"ant-design-vue": "4.0.0",
|
||||
"axios": "^0.21.1",
|
||||
"clipboard": "^2.0.8",
|
||||
"core-js": "^3.15.2",
|
||||
"dayjs": "^1.10.6",
|
||||
"js-cookie": "^3.0.0-rc.3",
|
||||
"mockjs": "^1.1.0",
|
||||
"qs": "^6.11.2",
|
||||
"remixicon": "^2.5.0",
|
||||
"vue": "^3.1.4",
|
||||
"vue-router": "^4.0.10",
|
||||
"vuex": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.5.9",
|
||||
"@vue/cli-plugin-eslint": "^4.5.9",
|
||||
"@vue/cli-service": "^4.5.9",
|
||||
"@vue/compiler-sfc": "^3.1.4",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"babel-eslint": "^11.0.0-beta.2",
|
||||
"body-parser": "^1.19.0",
|
||||
"chalk": "^4.1.1",
|
||||
"chokidar": "^3.5.2",
|
||||
"eslint": "^7.30.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-vue": "^7.13.0",
|
||||
"filemanager-webpack-plugin": "^6.1.4",
|
||||
"image-webpack-loader": "^7.0.1",
|
||||
"less": "^4.1.1",
|
||||
"less-loader": "^7.3.0",
|
||||
"prettier": "^2.3.2",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-recess-order": "^2.4.0",
|
||||
"svg-sprite-loader": "^6.0.9",
|
||||
"vab-config": "file:vab-config",
|
||||
"webpackbar": "^5.0.0-3"
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/chuzhixin/vue-admin-better.git"
|
||||
},
|
||||
"gitHooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
|
|
@ -60,5 +26,71 @@
|
|||
"vue-cli-service lint",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.3",
|
||||
"caniuse-lite": "^1.0.30001572",
|
||||
"clipboard": "^2.0.11",
|
||||
"core-js": "^3.35.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "5.4.3",
|
||||
"element-ui": "^2.15.14",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"layouts": "file:layouts",
|
||||
"lodash": "^4.17.21",
|
||||
"maptalks": "^0.49.5",
|
||||
"mapv": "^2.0.62",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"qs": "^6.11.2",
|
||||
"screenfull": "^5.2.0",
|
||||
"sortablejs": "^1.15.1",
|
||||
"vab-icon": "file:vab-icon",
|
||||
"vue": "~2.7.14",
|
||||
"vue-echarts": "6.6.8",
|
||||
"vue-router": "^3.6.5",
|
||||
"vue-template-compiler": "~2.7.14",
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.5.15",
|
||||
"@vue/cli-plugin-eslint": "^4.5.15",
|
||||
"@vue/cli-service": "^4.5.15",
|
||||
"@vue/composition-api": "^1.7.2",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-vue": "^9.1.1",
|
||||
"filemanager-webpack-plugin": "^8.0.0",
|
||||
"image-webpack-loader": "^8.1.0",
|
||||
"lint-staged": "^15.2.0",
|
||||
"prettier": "^2.8.8",
|
||||
"sass": "~1.32.13",
|
||||
"sass-loader": "^10.1.1",
|
||||
"stylelint": "^16.1.0",
|
||||
"stylelint-config-prettier": "^9.0.5",
|
||||
"stylelint-config-recess-order": "^4.4.0",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"webpack": "4.46.0",
|
||||
"webpackbar": "^6.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"vue",
|
||||
"admin",
|
||||
"dashboard",
|
||||
"element-ui",
|
||||
"vue-admin",
|
||||
"element-admin",
|
||||
"boilerplate",
|
||||
"admin-template",
|
||||
"management-system"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=8.9",
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
/**
|
||||
* @author https://vue-admin-beautiful.com (不想保留author可删除)
|
||||
* @description 代码规范
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
printWidth: 80,
|
||||
printWidth: 140,
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
semi: false,
|
||||
|
|
@ -8,7 +13,6 @@ module.exports = {
|
|||
jsxSingleQuote: false,
|
||||
trailingComma: 'es5',
|
||||
bracketSpacing: true,
|
||||
jsxBracketSameLine: false,
|
||||
arrowParens: 'always',
|
||||
htmlWhitespaceSensitivity: 'ignore',
|
||||
vueIndentScriptAndStyle: true,
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -1,39 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="zh-cmn-Hans">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
|
||||
<meta content="width=device-width,initial-scale=1.0" name="viewport" />
|
||||
<link href="<%= BASE_URL %>favicon.ico" rel="icon" />
|
||||
<title>vue-admin-better官网、首页、文档和下载 - 前端开发框架</title>
|
||||
<meta
|
||||
content="vab,vab官网,后台管理框架,vue后台管理框架,vue-admin-beautiful,vue-admin-beautiful-pro,vue-admin-beautiful官网,vue-admin-beautiful文档,vue-element-admin,vue-element-admin官网,vue-element-admin文档,vue-admin,vue-admin官网,vue-admin文档"
|
||||
content="vab,vab官网,后台管理框架,vue后台管理框架,vue-admin-beautiful-pro源码分享,vue-admin-beautiful-pro源码,vue-admin-beautiful官网,vue-admin-beautiful文档,vue-element-admin,vue-element-admin官网,vue-element-admin文档,vue-admin,vue-admin官网,vue-admin文档"
|
||||
name="keywords"
|
||||
/>
|
||||
<meta
|
||||
content="<%= VUE_APP_TITLE %>官网与文档基于vue-admin-beautiful-pro构建,简称vab(是一款超棒的vue+element中后台前端快速开发框架),QQ群972435319,作者:<%= VUE_APP_AUTHOR %>"
|
||||
content="<%= VUE_APP_TITLE %>官网与文档基于vue-admin-beautiful构建,简称vab(是一款超棒的vue+element中后台前端快速开发框架),QQ群972435319,作者:<%= VUE_APP_AUTHOR %>"
|
||||
name="description"
|
||||
/>
|
||||
<meta content="<%= VUE_APP_AUTHOR %>" name="author" />
|
||||
<link href="<%= BASE_URL %>static/css/loading.css" rel="stylesheet" />
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?7174bade1219f9cc272e7978f9523fc8";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>
|
||||
We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
properly without JavaScript enabled. Please enable it to continue.
|
||||
</strong>
|
||||
非常抱歉鉴于安全考量,您无法查看<%= VUE_APP_TITLE %>
|
||||
源代码,该系统基于vue-admin-better开发
|
||||
</noscript>
|
||||
<div id="app">
|
||||
<div id="vue-admin-beautiful">
|
||||
<div class="first-loading-wrp">
|
||||
<div class="loading-wrp">
|
||||
<span class="dot dot-spin">
|
||||
|
|
@ -46,6 +35,22 @@
|
|||
<h1><%= VUE_APP_TITLE %></h1>
|
||||
</div>
|
||||
</div>
|
||||
<!-- built files will be auto injected -->
|
||||
<script>
|
||||
;/^http(s*):\/\//.test(location.href) ||
|
||||
alert(
|
||||
'基于vue-admin-beautiful-pro开源版开发的项目需要部署到服务器下访问'
|
||||
)
|
||||
</script>
|
||||
<script>
|
||||
if (window.location.hostname !== 'localhost') {
|
||||
var _hmt = _hmt || []
|
||||
;(function () {
|
||||
var hm = document.createElement('script')
|
||||
hm.src = 'https://hm.baidu.com/hm.js?7174bade1219f9cc272e7978f9523fc8'
|
||||
var s = document.getElementsByTagName('script')[0]
|
||||
s.parentNode.insertBefore(hm, s)
|
||||
})()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
/**
|
||||
* @description 雪花屏代码,基于ant-design修改
|
||||
**/
|
||||
.first-loading-wrp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
@ -8,7 +11,7 @@
|
|||
}
|
||||
|
||||
.first-loading-wrp > h1 {
|
||||
font-size: 24px;
|
||||
font-size: 30px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
#强制推送
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
git init
|
||||
git add -A
|
||||
git commit -m '🎉 feat: init project'
|
||||
git push -f "https://${access_token}@github.com/chuzhixin/vue-admin-better.git" master
|
||||
exec /bin/bash
|
||||
|
||||
|
||||
|
||||
|
||||
16
src/App.vue
|
|
@ -1,22 +1,12 @@
|
|||
<template>
|
||||
<div id="vue-admin-beautiful">
|
||||
<a-config-provider :locale="locale">
|
||||
<router-view />
|
||||
</a-config-provider>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
data() {
|
||||
return {
|
||||
locale: zhCN,
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '~@/vab/styles/vab.less';
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(data) {
|
||||
return request({
|
||||
//url: '/ad/getList',
|
||||
url: 'https://851edf02-46eb-43e6-828d-64c7e483ea41.bspapp.com/http/getAd',
|
||||
method: 'get',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(data) {
|
||||
return request({
|
||||
// url: '/changeLog/getList',
|
||||
url: 'https://851edf02-46eb-43e6-828d-64c7e483ea41.bspapp.com/http/getChangeLog',
|
||||
method: 'get',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getIconList(data) {
|
||||
return request({
|
||||
url: '/colorfulIcon/getList',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import request from 'axios'
|
||||
|
||||
export function getRepos(params) {
|
||||
return request({
|
||||
url: 'https://api.github.com/repos/chuzhixin/vue-admin-beautiful',
|
||||
method: 'get',
|
||||
params,
|
||||
timeout: 10000,
|
||||
})
|
||||
}
|
||||
|
||||
export function getStargazers(params) {
|
||||
return request({
|
||||
url: 'https://api.github.com/repos/chuzhixin/vue-admin-beautiful/stargazers',
|
||||
method: 'get',
|
||||
params,
|
||||
timeout: 10000,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(data) {
|
||||
return request({
|
||||
url: '/goodsList/getList',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getIconList(params) {
|
||||
export function getIconList(data) {
|
||||
return request({
|
||||
url: '/icon/getList',
|
||||
method: 'get',
|
||||
params,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
import request from 'axios'
|
||||
|
||||
export function getList() {
|
||||
return request({
|
||||
url: 'https://fastly.jsdelivr.net/gh/prettier/prettier@master/docs/options.md',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getTree(data) {
|
||||
return request({
|
||||
url: '/menuManagement/getTree',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doEdit(data) {
|
||||
return request({
|
||||
url: '/menuManagement/doEdit',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doDelete(data) {
|
||||
return request({
|
||||
url: '/menuManagement/doDelete',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getNoticeList() {
|
||||
return request({
|
||||
// url: '/notice/getList',
|
||||
url: 'https://851edf02-46eb-43e6-828d-64c7e483ea41.bspapp.com/http/getNotice',
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(data) {
|
||||
return request({
|
||||
url: '/personalCenter/getList',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doEdit(data) {
|
||||
return request({
|
||||
url: '/personalCenter/doEdit',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doDelete(data) {
|
||||
return request({
|
||||
url: '/personalCenter/doDelete',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getPublicKey() {
|
||||
return request({
|
||||
url: '/publicKey',
|
||||
method: 'post',
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getIconList(data) {
|
||||
return request({
|
||||
url: '/remixIcon/getList',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(data) {
|
||||
return request({
|
||||
url: '/roleManagement/getList',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doEdit(data) {
|
||||
return request({
|
||||
url: '/roleManagement/doEdit',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doDelete(data) {
|
||||
return request({
|
||||
url: '/roleManagement/doDelete',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getRouterList(params) {
|
||||
export function getRouterList(data) {
|
||||
return request({
|
||||
url: '/menu/navigate',
|
||||
method: 'get',
|
||||
params,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(params) {
|
||||
export function getList(data) {
|
||||
return request({
|
||||
url: '/table/getList',
|
||||
method: 'get',
|
||||
params,
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getTreeList(data) {
|
||||
return request({
|
||||
url: '/tree/list',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
import request from '@/utils/request'
|
||||
import { tokenName } from '@/config'
|
||||
import { encryptedData } from '@/utils/encrypt'
|
||||
import { loginRSA, tokenName } from '@/config'
|
||||
|
||||
export async function login(data) {
|
||||
if (loginRSA) {
|
||||
data = await encryptedData(data)
|
||||
}
|
||||
return request({
|
||||
url: '/login',
|
||||
method: 'post',
|
||||
|
|
@ -9,16 +13,7 @@ export async function login(data) {
|
|||
})
|
||||
}
|
||||
|
||||
export async function socialLogin(data) {
|
||||
return request({
|
||||
url: '/socialLogin',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function getUserInfo(accessToken) {
|
||||
//此处为了兼容mock.js使用data传递accessToken,如果使用mock可以走headers
|
||||
return request({
|
||||
url: '/userInfo',
|
||||
method: 'post',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function getList(data) {
|
||||
return request({
|
||||
url: '/userManagement/getList',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doEdit(data) {
|
||||
return request({
|
||||
url: '/userManagement/doEdit',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function doDelete(data) {
|
||||
return request({
|
||||
url: '/userManagement/doDelete',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
|
@ -0,0 +1,13 @@
|
|||
const req = require.context('./svg', false, /\.svg$/),
|
||||
requireAll = (requireContext) => {
|
||||
/*let a = requireContext.keys().map(requireContext);
|
||||
let arr = [];
|
||||
for (let i = 0; i < a.length; i++) {
|
||||
console.log();
|
||||
let icon = a[i].default.id;
|
||||
arr.push(icon);
|
||||
}
|
||||
console.log(JSON.stringify(arr));*/
|
||||
return requireContext.keys().map(requireContext)
|
||||
}
|
||||
requireAll(req)
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<svg class="icon" width="128" height="128" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M358.4 853.333H245.333l-23.466 64H147.2l121.6-324.266h61.867l119.466 324.266h-68.266l-23.467-64zm-98.133-57.6h81.066l-40.533-121.6-40.533 121.6zm4.266-418.133h162.134v53.333H179.2V390.4L341.333 160H179.2v-53.333h243.2v36.266L264.533 377.6z"
|
||||
fill="#2196F3"/>
|
||||
<path d="M810.667 704V106.667h-85.334V704h-128L768 917.333 938.667 704z" fill="#546E7A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 479 B |
|
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="550px" height="400px"
|
||||
xml:space="preserve">
|
||||
<g id="PathID_1" transform="matrix(10.7099, 0, 0, 10.7099, 76.4, 396.15)" opacity="1">
|
||||
<path style="fill: #41b882; fill-opacity: 1;"
|
||||
d="M3.75 -36.65L18.4 -36.65Q22.75 -36.65 24.85 -36.25Q27 -35.9 28.7 -34.75Q30.4 -33.6 31.5 -31.7Q32.65 -29.8 32.65 -27.4Q32.65 -24.85 31.25 -22.7Q29.85 -20.55 27.5 -19.5Q30.85 -18.5 32.65 -16.15Q34.45 -13.8 34.45 -10.6Q34.45 -8.1 33.25 -5.75Q32.1 -3.4 30.1 -1.95Q28.1 -0.55 25.15 -0.25Q23.3 -0.05 16.2 0L3.75 0L3.75 -36.65M11.15 -30.55L11.15 -22.1L16 -22.1Q20.3 -22.1 21.35 -22.2Q23.25 -22.4 24.35 -23.5Q25.45 -24.6 25.45 -26.35Q25.45 -28.05 24.5 -29.1Q23.55 -30.2 21.7 -30.4Q20.6 -30.55 15.4 -30.55L11.15 -30.55M11.15 -16L11.15 -6.2L18 -6.2Q22 -6.2 23.05 -6.4Q24.7 -6.7 25.75 -7.85Q26.8 -9.05 26.8 -11Q26.8 -12.65 26 -13.8Q25.2 -14.95 23.65 -15.45Q22.15 -16 17.1 -16L11.15 -16"/>
|
||||
</g>
|
||||
<g id="PathID_2" transform="matrix(10.7099, 0, 0, 10.7099, 76.4, 396.15)" opacity="1">
|
||||
</g>
|
||||
<g id="PathID_3" transform="matrix(5.31826, 0, 0, 2.59618, 172.9, 161.55)" opacity="1">
|
||||
<path style="fill: #35495e; fill-opacity: 1;"
|
||||
d="M3.75 -36.65L17.25 -36.65Q21.8 -36.65 24.2 -35.95Q27.45 -35 29.75 -32.55Q32.05 -30.15 33.25 -26.6Q34.45 -23.1 34.45 -17.95Q34.45 -13.45 33.3 -10.15Q31.95 -6.15 29.4 -3.7Q27.45 -1.8 24.2 -0.75Q21.75 0 17.65 0L3.75 0L3.75 -36.65M11.15 -30.45L11.15 -6.2L16.65 -6.2Q19.75 -6.2 21.1 -6.55Q22.9 -6.95 24.1 -8Q25.3 -9.1 26.05 -11.55Q26.8 -14.05 26.8 -18.3Q26.8 -22.55 26.05 -24.8Q25.3 -27.1 23.95 -28.35Q22.6 -29.65 20.5 -30.1Q18.95 -30.45 14.45 -30.45L11.15 -30.45"/>
|
||||
</g>
|
||||
<g id="PathID_4" transform="matrix(5.31826, 0, 0, 2.59618, 172.9, 161.55)" opacity="1">
|
||||
</g>
|
||||
<g id="PathID_5" transform="matrix(5.78477, 0, 0, 3.1825, 171.7, 333.8)" opacity="1">
|
||||
<path style="fill: #35495e; fill-opacity: 1;"
|
||||
d="M3.75 -36.65L17.25 -36.65Q21.8 -36.65 24.2 -35.95Q27.45 -35 29.75 -32.55Q32.05 -30.15 33.25 -26.6Q34.45 -23.1 34.45 -17.95Q34.45 -13.45 33.3 -10.15Q31.95 -6.15 29.4 -3.7Q27.45 -1.8 24.2 -0.75Q21.75 0 17.65 0L3.75 0L3.75 -36.65M11.15 -30.45L11.15 -6.2L16.65 -6.2Q19.75 -6.2 21.1 -6.55Q22.9 -6.95 24.1 -8Q25.3 -9.1 26.05 -11.55Q26.8 -14.05 26.8 -18.3Q26.8 -22.55 26.05 -24.8Q25.3 -27.1 23.95 -28.35Q22.6 -29.65 20.5 -30.1Q18.95 -30.45 14.45 -30.45L11.15 -30.45"/>
|
||||
</g>
|
||||
<g id="PathID_6" transform="matrix(5.78477, 0, 0, 3.1825, 171.7, 333.8)" opacity="1">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
|
|
@ -0,0 +1,187 @@
|
|||
<template>
|
||||
<div class="select-tree-template">
|
||||
<el-select
|
||||
v-model="selectValue"
|
||||
class="vab-tree-select"
|
||||
:clearable="clearable"
|
||||
:collapse-tags="selectType == 'multiple'"
|
||||
:multiple="selectType == 'multiple'"
|
||||
value-key="id"
|
||||
@clear="clearHandle"
|
||||
@remove-tag="removeTag"
|
||||
>
|
||||
<el-option :value="selectKey">
|
||||
<el-tree
|
||||
id="treeOption"
|
||||
ref="treeOption"
|
||||
:current-node-key="currentNodeKey"
|
||||
:data="treeOptions"
|
||||
:default-checked-keys="defaultSelectedKeys"
|
||||
:default-expanded-keys="defaultSelectedKeys"
|
||||
:highlight-current="true"
|
||||
node-key="id"
|
||||
:props="defaultProps"
|
||||
:show-checkbox="selectType == 'multiple'"
|
||||
@check="checkNode"
|
||||
@node-click="nodeClick"
|
||||
/>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SelectTreeTemplate',
|
||||
props: {
|
||||
/* 树形结构数据 */
|
||||
treeOptions: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
},
|
||||
},
|
||||
/* 单选/多选 */
|
||||
selectType: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return 'single'
|
||||
},
|
||||
},
|
||||
/* 初始选中值key */
|
||||
selectedKey: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return ''
|
||||
},
|
||||
},
|
||||
/* 初始选中值name */
|
||||
selectedValue: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return ''
|
||||
},
|
||||
},
|
||||
/* 可做选择的层级 */
|
||||
selectLevel: {
|
||||
type: [String, Number],
|
||||
default: () => {
|
||||
return ''
|
||||
},
|
||||
},
|
||||
/* 可清空选项 */
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return true
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
},
|
||||
defaultSelectedKeys: [], //初始选中值数组
|
||||
currentNodeKey: this.selectedKey,
|
||||
selectValue: this.selectType == 'multiple' ? this.selectedValue.split(',') : this.selectedValue, //下拉框选中值label
|
||||
selectKey: this.selectType == 'multiple' ? this.selectedKey.split(',') : this.selectedKey, //下拉框选中值value
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initTree()
|
||||
},
|
||||
methods: {
|
||||
// 初始化树的值
|
||||
initTree() {
|
||||
const that = this
|
||||
if (that.selectedKey) {
|
||||
that.defaultSelectedKeys = that.selectedKey.split(',') // 设置默认展开
|
||||
if (that.selectType == 'single') {
|
||||
that.$refs.treeOption.setCurrentKey(that.selectedKey) // 设置默认选中
|
||||
} else {
|
||||
that.$refs.treeOption.setCheckedKeys(that.defaultSelectedKeys)
|
||||
}
|
||||
}
|
||||
},
|
||||
// 清除选中
|
||||
clearHandle() {
|
||||
const that = this
|
||||
this.selectValue = ''
|
||||
this.selectKey = ''
|
||||
this.defaultSelectedKeys = []
|
||||
this.currentNodeKey = ''
|
||||
this.clearSelected()
|
||||
if (that.selectType == 'single') {
|
||||
that.$refs.treeOption.setCurrentKey('') // 设置默认选中
|
||||
} else {
|
||||
that.$refs.treeOption.setCheckedKeys([])
|
||||
}
|
||||
},
|
||||
/* 清空选中样式 */
|
||||
clearSelected() {
|
||||
const allNode = document.querySelectorAll('#treeOption .el-tree-node')
|
||||
allNode.forEach((element) => element.classList.remove('is-current'))
|
||||
},
|
||||
// select多选时移除某项操作
|
||||
removeTag() {
|
||||
this.$refs.treeOption.setCheckedKeys([])
|
||||
},
|
||||
// 点击叶子节点
|
||||
nodeClick(data) {
|
||||
if (data.rank >= this.selectLevel) {
|
||||
this.selectValue = data.name
|
||||
this.selectKey = data.id
|
||||
}
|
||||
},
|
||||
// 节点选中操作
|
||||
checkNode() {
|
||||
const checkedNodes = this.$refs.treeOption.getCheckedNodes()
|
||||
const keyArr = []
|
||||
const valueArr = []
|
||||
checkedNodes.forEach((item) => {
|
||||
if (item.rank >= this.selectLevel) {
|
||||
keyArr.push(item.id)
|
||||
valueArr.push(item.name)
|
||||
}
|
||||
})
|
||||
this.selectValue = valueArr
|
||||
this.selectKey = keyArr
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
|
||||
height: auto;
|
||||
max-height: 274px;
|
||||
padding: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.selected {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul li > .el-tree .el-tree-node__content {
|
||||
height: auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.el-tree-node__label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.el-tree > .is-current .el-tree-node__label {
|
||||
font-weight: 700;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.el-tree > .is-current .el-tree-node__children .el-tree-node__label {
|
||||
font-weight: normal;
|
||||
color: #606266;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<template>
|
||||
<div class="content">
|
||||
<div class="g-container" :style="styleObj">
|
||||
<div class="g-number">
|
||||
{{ endVal }}
|
||||
</div>
|
||||
<div class="g-contrast">
|
||||
<div class="g-circle"></div>
|
||||
<ul class="g-bubbles">
|
||||
<li v-for="(item, index) in 15" :key="index"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabCharge',
|
||||
props: {
|
||||
styleObj: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
startVal: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
endVal: {
|
||||
type: Number,
|
||||
default: 100,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
decimals: 2,
|
||||
prefix: '',
|
||||
suffix: '%',
|
||||
separator: ',',
|
||||
duration: 3000,
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center; /* 垂直居中 */
|
||||
justify-content: center; /* 水平居中 */
|
||||
width: 100%;
|
||||
background: #000;
|
||||
|
||||
.g-number {
|
||||
position: absolute;
|
||||
top: 27%;
|
||||
z-index: 99;
|
||||
width: 300px;
|
||||
font-size: 32px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.g-container {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.g-contrast {
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
filter: contrast(15) hue-rotate(0);
|
||||
animation: hueRotate 10s infinite linear;
|
||||
}
|
||||
|
||||
.g-circle {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
filter: blur(8px);
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
content: '';
|
||||
background-color: #00ff6f;
|
||||
border-radius: 42% 38% 62% 49% / 45%;
|
||||
transform: translate(-50%, -50%) rotate(0);
|
||||
animation: rotate 10s infinite linear;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
z-index: 99;
|
||||
width: 176px;
|
||||
height: 176px;
|
||||
content: '';
|
||||
background-color: #000;
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.g-bubbles {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
background-color: #00ff6f;
|
||||
filter: blur(5px);
|
||||
border-radius: 100px 100px 0 0;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
li {
|
||||
position: absolute;
|
||||
background: #00ff6f;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@for $i from 0 through 15 {
|
||||
li:nth-child(#{$i}) {
|
||||
$width: 15 + random(15) + px;
|
||||
|
||||
top: 50%;
|
||||
left: 15 + random(70) + px;
|
||||
width: $width;
|
||||
height: $width;
|
||||
transform: translate(-50%, -50%);
|
||||
animation: moveToTop #{random(6) + 3}s ease-in-out -#{random(5000) / 1000}s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
50% {
|
||||
border-radius: 45% / 42% 38% 58% 49%;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(720deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveToTop {
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
transform: translate(-50%, -180px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hueRotate {
|
||||
100% {
|
||||
filter: contrast(15) hue-rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
<template>
|
||||
<div class="card" :style="styleObj">
|
||||
<div class="card-borders">
|
||||
<div class="border-top"></div>
|
||||
<div class="border-right"></div>
|
||||
<div class="border-bottom"></div>
|
||||
<div class="border-left"></div>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<el-image class="avatar" :src="avatar" />
|
||||
<div class="username">
|
||||
{{ username }}
|
||||
</div>
|
||||
<div class="social-icons">
|
||||
<a v-for="(item, index) in iconArray" :key="index" class="social-icon" :href="item.url" target="_blank">
|
||||
<vab-icon :icon="['fas', item.icon]" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabProfile',
|
||||
props: {
|
||||
styleObj: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
iconArray: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [
|
||||
{ icon: 'bell', url: '' },
|
||||
{ icon: 'bookmark', url: '' },
|
||||
{ icon: 'cloud-sun', url: '' },
|
||||
]
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card {
|
||||
--card-bg-color: hsl(240, 31%, 25%);
|
||||
--card-bg-color-transparent: hsla(240, 31%, 25%, 0.7);
|
||||
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.card-borders {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.border-top {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--card-bg-color);
|
||||
transform: translateX(-100%);
|
||||
animation: slide-in-horizontal 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) forwards;
|
||||
}
|
||||
|
||||
.border-right {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--card-bg-color);
|
||||
transform: translateY(100%);
|
||||
animation: slide-in-vertical 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) forwards;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--card-bg-color);
|
||||
transform: translateX(100%);
|
||||
animation: slide-in-horizontal-reverse 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) forwards;
|
||||
}
|
||||
|
||||
.border-left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--card-bg-color);
|
||||
transform: translateY(-100%);
|
||||
animation: slide-in-vertical-reverse 0.8s cubic-bezier(0.645, 0.045, 0.355, 1) forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 40px 0 40px 0;
|
||||
background: var(--card-bg-color-transparent);
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
animation: bump-in 0.5s 0.8s forwards;
|
||||
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 1px solid $base-color-white;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
animation: bump-in 0.5s 1s forwards;
|
||||
}
|
||||
|
||||
.username {
|
||||
position: relative;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 26px;
|
||||
color: transparent;
|
||||
letter-spacing: 2px;
|
||||
animation: fill-text-white 1.2s 2s forwards;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: black;
|
||||
content: '';
|
||||
background: #35b9f1;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
animation: slide-in-out 1.2s 1.2s cubic-bezier(0.75, 0, 0, 1) forwards;
|
||||
}
|
||||
}
|
||||
|
||||
.social-icons {
|
||||
display: flex;
|
||||
|
||||
.social-icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
margin: 0 15px;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 50%;
|
||||
|
||||
@for $i from 1 through 3 {
|
||||
&:nth-child(#{$i}) {
|
||||
&::before {
|
||||
animation-delay: 2s + 0.1s * $i;
|
||||
}
|
||||
|
||||
&::after {
|
||||
animation-delay: 2.1s + 0.1s * $i;
|
||||
}
|
||||
|
||||
svg {
|
||||
animation-delay: 2.2s + 0.1s * $i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
border-radius: inherit;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
&::before {
|
||||
background: #f7f1e3;
|
||||
animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background: #2c3e50;
|
||||
animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards;
|
||||
}
|
||||
|
||||
svg {
|
||||
z-index: 99;
|
||||
transform: scale(0);
|
||||
animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bump-in {
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in-horizontal {
|
||||
50% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in-horizontal-reverse {
|
||||
50% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in-vertical {
|
||||
50% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in-vertical-reverse {
|
||||
50% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-in-out {
|
||||
50% {
|
||||
transform: scaleX(1);
|
||||
transform-origin: left;
|
||||
}
|
||||
|
||||
50.1% {
|
||||
transform-origin: right;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scaleX(0);
|
||||
transform-origin: right;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fill-text-white {
|
||||
to {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scale-in {
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<template>
|
||||
<div class="content" :style="styleObj">
|
||||
<div v-for="(item, index) in 200" :key="index" class="snow"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabSnow',
|
||||
props: {
|
||||
styleObj: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.content {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
|
||||
filter: drop-shadow(0 0 10px white);
|
||||
}
|
||||
|
||||
@function random_range($min, $max) {
|
||||
$rand: random();
|
||||
$random_range: $min + floor($rand * (($max - $min) + 1));
|
||||
|
||||
@return $random_range;
|
||||
}
|
||||
|
||||
.snow {
|
||||
$total: 200;
|
||||
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
|
||||
@for $i from 1 through $total {
|
||||
$random-x: random(1000000) * 0.0001vw;
|
||||
$random-offset: random_range(-100000, 100000) * 0.0001vw;
|
||||
$random-x-end: $random-x + $random-offset;
|
||||
$random-x-end-yoyo: $random-x + ($random-offset / 2);
|
||||
$random-yoyo-time: random_range(30000, 80000) / 100000;
|
||||
$random-yoyo-y: $random-yoyo-time * 100vh;
|
||||
$random-scale: random(10000) * 0.0001;
|
||||
$fall-duration: random_range(10, 30) * 1s;
|
||||
$fall-delay: random(30) * -1s;
|
||||
|
||||
&:nth-child(#{$i}) {
|
||||
opacity: random(10000) * 0.0001;
|
||||
transform: translate($random-x, -10px) scale($random-scale);
|
||||
animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
|
||||
}
|
||||
|
||||
@keyframes fall-#{$i} {
|
||||
#{percentage($random-yoyo-time)} {
|
||||
transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||