Repository: chuzhixin/vue-admin-beautiful-template Branch: master Commit: 2d22a04f23e5 Files: 114 Total size: 281.4 KB Directory structure: gitextract_nkjw1dck/ ├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .stylelintrc.js ├── .vscode/ │ └── settings.json ├── LICENSE ├── README.en.md ├── README.md ├── babel.config.js ├── deploy.sh ├── http/ │ └── mock.http ├── layouts/ │ ├── Permissions/ │ │ ├── index.js │ │ └── permissions.js │ ├── VabColorfullIcon/ │ │ └── index.vue │ ├── VabErrorLog/ │ │ └── index.vue │ ├── VabFullScreenBar/ │ │ └── index.vue │ ├── VabGithubCorner/ │ │ └── index.vue │ ├── VabQueryForm/ │ │ ├── VabQueryFormBottomPanel.vue │ │ ├── VabQueryFormLeftPanel.vue │ │ ├── VabQueryFormRightPanel.vue │ │ ├── VabQueryFormTopPanel.vue │ │ └── index.vue │ ├── VabRemixIcon/ │ │ └── index.vue │ ├── VabSideBar/ │ │ ├── components/ │ │ │ ├── VabMenuItem.vue │ │ │ ├── VabSideBarItem.vue │ │ │ └── VabSubmenu.vue │ │ └── index.vue │ ├── VabTabsBar/ │ │ └── index.vue │ ├── VabTopBar/ │ │ └── index.vue │ ├── index.js │ ├── package.json │ └── prettier.config.js ├── license.md ├── mock/ │ ├── controller/ │ │ ├── ad.js │ │ ├── router.js │ │ └── user.js │ ├── index.js │ ├── mockServer.js │ └── utils/ │ └── index.js ├── package.json ├── plopfile.js ├── prettier.config.js ├── public/ │ ├── index.html │ └── static/ │ └── css/ │ └── loading.css ├── push.sh ├── src/ │ ├── App.vue │ ├── api/ │ │ ├── ad.js │ │ ├── publicKey.js │ │ ├── router.js │ │ └── user.js │ ├── colorfulIcon/ │ │ └── index.js │ ├── config/ │ │ ├── index.js │ │ ├── net.config.js │ │ ├── permission.js │ │ ├── setting.config.js │ │ ├── settings.js │ │ └── theme.config.js │ ├── layouts/ │ │ ├── EmptyLayout.vue │ │ ├── components/ │ │ │ ├── VabAd/ │ │ │ │ └── index.vue │ │ │ ├── VabAppMain/ │ │ │ │ └── index.vue │ │ │ ├── VabAvatar/ │ │ │ │ └── index.vue │ │ │ ├── VabBreadcrumb/ │ │ │ │ └── index.vue │ │ │ ├── VabLogo/ │ │ │ │ └── index.vue │ │ │ ├── VabNavBar/ │ │ │ │ └── index.vue │ │ │ └── VabThemeBar/ │ │ │ └── index.vue │ │ ├── export.js │ │ └── index.vue │ ├── main.js │ ├── plugins/ │ │ ├── element.js │ │ ├── index.js │ │ ├── support.js │ │ └── vabIcon.js │ ├── remixIcon/ │ │ └── index.js │ ├── router/ │ │ └── index.js │ ├── store/ │ │ ├── index.js │ │ └── modules/ │ │ ├── errorLog.js │ │ ├── routes.js │ │ ├── settings.js │ │ ├── table.js │ │ ├── tabsBar.js │ │ └── user.js │ ├── styles/ │ │ ├── element-variables.scss │ │ ├── loading.scss │ │ ├── normalize.scss │ │ ├── spinner/ │ │ │ ├── dots.css │ │ │ ├── gauge.css │ │ │ ├── inner-circles.css │ │ │ └── plus.css │ │ ├── themes/ │ │ │ └── default.scss │ │ ├── transition.scss │ │ ├── vab.scss │ │ └── variables.scss │ ├── utils/ │ │ ├── accessToken.js │ │ ├── encrypt.js │ │ ├── errorLog.js │ │ ├── handleRoutes.js │ │ ├── index.js │ │ ├── pageTitle.js │ │ ├── permission.js │ │ ├── request.js │ │ ├── static.js │ │ ├── vab.js │ │ └── validate.js │ └── views/ │ ├── 401.vue │ ├── 404.vue │ ├── index/ │ │ └── index.vue │ ├── login/ │ │ └── index.vue │ └── register/ │ └── index.vue ├── vab-icon/ │ └── package.json ├── vue.config.js └── webstorm.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .browserslistrc ================================================ > 1% last 2 versions not dead ================================================ FILE: .editorconfig ================================================ 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 ================================================ FILE: .eslintignore ================================================ src/assets src/icons public dist node_modules ================================================ FILE: .eslintrc.js ================================================ module.exports = { root: true, env: { node: true, }, extends: ['plugin:vue/recommended', '@vue/prettier'], rules: { 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 'vue/no-v-html': 'off', }, parserOptions: { parser: 'babel-eslint', }, overrides: [ { files: [ '**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)', ], env: { jest: true, }, }, ], } ================================================ FILE: .gitattributes ================================================ *.html text eol=lf *.css text eol=lf *.js text eol=lf *.scss text eol=lf *.vue text eol=lf *.hbs text eol=lf *.sh text eol=lf *.md text eol=lf *.json text eol=lf *.yml text eol=lf ================================================ FILE: .gitignore ================================================ .DS_Store node_modules dist .env.local .env.*.local npm-debug.log* yarn-debug.log* yarn-error.log* .idea *.suo *.ntvs* *.njsproj *.sln *.sw? public/video *.zip *.7z /src/layouts/components/zx-layouts /zx-templates /src/styles/themes/glory.scss /src/styles/themes/green.scss /src/styles/themes/dark.scss ================================================ FILE: .stylelintrc.js ================================================ module.exports = { extends: ['stylelint-config-recess-order', 'stylelint-config-prettier'], } ================================================ FILE: .vscode/settings.json ================================================ { "[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": true, "source.fixAll.eslint": true }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[scss]": { "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": "chuzhixin 1204505056@qq.com", "docthis.includeAuthorTag": true, "docthis.includeDescriptionTag": true, "docthis.enableHungarianNotationEvaluation": true, "docthis.inferTypesFromNames": true, "vetur.format.defaultFormatter.html": "prettier" } ================================================ FILE: LICENSE ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: README.en.md ================================================ [简体中文](./README.md) | English

vue-admin-better(element-ui)

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.

[![stars](https://img.shields.io/github/stars/chuzhixin/vue-admin-beautiful?style=flat-square&logo=GitHub)](https://github.com/chuzhixin/vue-admin-beautiful) [![star](https://gitee.com/chu1204505056/vue-admin-better/badge/star.svg?theme=gray)](https://gitee.com/chu1204505056/vue-admin-better) [![license](https://img.shields.io/github/license/chuzhixin/vue-admin-beautiful?style=flat-square)](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.
## 🙈 We promise to sponsor open source projects regularly (thank giant) ## 🎨 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) ## 📌 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 ![img](https://fastly.jsdelivr.net/gh/chuzhixin/image/vip/flow.drawio.png) ## 🗃️ design sketch The following is a screenshot of the pro version:
## 📄 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. ================================================ FILE: README.md ================================================
简体中文 | [English](./README.en.md)

vue-admin-better

众志成城,攻坚克难,愿所有美好纷沓而来!

[![stars](https://img.shields.io/github/stars/chuzhixin/vue-admin-beautiful?style=flat-square&logo=GitHub)](https://github.com/chuzhixin/vue-admin-beautiful) [![star](https://gitee.com/chu1204505056/vue-admin-better/badge/star.svg?theme=gray)](https://gitee.com/chu1204505056/vue-admin-better) [![license](https://img.shields.io/github/license/chuzhixin/vue-admin-beautiful?style=flat-square)](https://en.wikipedia.org/wiki/MIT_License) --- ## 🎉 特性 - 💪 40+高质量单页 - 💅 RBAC 模型 + JWT 权限控制 - 🌍 10 万+ 项目实际应用 - 👏 良好的类型定义 - 🥳 开源版本支持免费商用 - 🚀 跨平台 PC、手机端、平板 - 📦️ 后端路由动态渲染 ## 🌐 地址 - [🎉 vue2.x + element-ui(免费商用,支持 PC、平板、手机)](https://vue-admin-beautiful.com/vue-admin-beautiful-element/) - [⚡️ vue3.x + element-plus(alpha 版本,免费商用,支持 PC、平板、手机)](https://vue-admin-beautiful.com/vue-admin-beautiful-element-plus/) - [⚡️ vue3.x + ant-design-vue(beta 版本,免费商用,支持 PC、平板、手机)](https://vue-admin-beautiful.com/vue-admin-beautiful-antdv/) - [⚡️ vue3.x + vite + arco](https://vue-admin-beautiful.com/vue-admin-arco/) - [🚀 admin pro 演示地址(vue2.x 付费版本,支持 PC、平板、手机)](https://vue-admin-beautiful.com/admin-pro/) - [🚀 admin plus 演示地址(vue3.x 付费版本,支持 PC、平板、手机)](https://vue-admin-beautiful.com/admin-plus/) - [📌 pro 及 plus 购买地址 authorization](https://vue-admin-beautiful.com/authorization/) - [🚀 Vue Shop Vite 商城(付费版本)](https://vue-admin-beautiful.com/shop-vite/) - [🌐 github 仓库地址](https://github.com/chuzhixin/vue-admin-beautiful?utm_source=gold_browser_extension) - [🌐 码云仓库地址](https://gitee.com/chu1204505056/vue-admin-better?_from=gitee_search) ## 🌐 备份地址 - [🚀 admin pro 演示地址(付费版本,支持 PC、平板、手机)](https://chu1204505056.gitee.io/admin-pro/) - [🚀 admin plus 演示地址(vue3.x 付费版本,支持 PC、平板、手机)](https://chu1204505056.gitee.io/admin-plus/) ## 🍻 前端讨论 QQ 群 - 请我们喝杯咖啡,打赏后联系 QQ 783963206 邀请您进入讨论群(由于用户数较多,如果您打赏后未通过好友请求,请联系商家),不管您请还是不请,您都可以享受到开源的代码,感谢您的支持和信任,群内提供 vue-admin-better 基础版本、开发工具自动配置教程及项目开发文档。
## 📦️ 桌面应用程序 - [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-better.git # 安装依赖 npm i --registry=http://mirrors.cloud.tencent.com/npm/ # 本地开发 启动项目 npm run serve ``` ## 🌱 vue3.x arco-design [点击切换仓库](https://github.com/chuzhixin/vue-admin-arco) ```bash # 克隆项目 git clone https://github.com/chuzhixin/vue-admin-arco.git # 安装依赖 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 ``` ## 🔊 友情链接 - [OPSLI 基于 vue-admin-better 开源版的最佳实践](https://github.com/hiparker/opsli-boot) - [uView uni-app 生态最优秀的 UI 框架](https://github.com/YanxinNet/uView/) - [form-generator Element 表单设计代码生成器](https://github.com/JakHuang/form-generator/) - [wangEditor 国产最强开源富文本编辑](https://github.com/wangeditor-team/wangEditor) ## 🙈 我们承诺将定期赞助的开源项目(感谢巨人) ## 🎨 鸣谢 | 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) | | [wangEditor](https://github.com/wangeditor-team/wangEditor) | ## 👷 框架杰出贡献者(排名不分先后) ## 📌 优势及注意事项 ``` 对比其他开源 admin 框架有如下优势: 1. 支持前端控制路由权限 intelligence、后端控制路由权限 all 模式 2. 已知开源 vue admin 框架中首家支持 mock 自动生成自动导出功能 3. 提供 50 余项全局精细化配置 4. 支持 scss 自动排序,eslint 自动修复 5. axios 精细化封装,支持多数据源、多成功 code 数组,支持 application/json;charset=UTF-8、application/x-www-form-urlencoded;charset=UTF-8 多种传参方式 6. 支持登录RSA加密 7. 支持打包自动生成7Z压缩包 8. 支持errorlog错误拦截 9. 支持多主题、多布局切换 使用注意事项: 1. 项目默认使用lf换行符而非crlf换行符,新建文件时请注意选择文件换行符 2. 项目默认使用的最严格的eslint校验规范(plugin:vue/recommended),使用之前建议配置开发工具实现自动修复(建议使用vscode开发) 3. 项目使用的是要求最宽泛的MIT开源协议,保留MIT开源协议即可免费商用 ``` ## 💚 适合人群 - 正在以及想使用 element-ui/element-plus 开发,前端开发经验 1 年+。 - 熟悉 Vue.js 技术栈,使用它开发过几个实际项目。 - 对原理技术感兴趣,想进阶和提升的同学。 ## 🎉 功能地图 ![img](https://fastly.jsdelivr.net/gh/chuzhixin/image/vip/flow.drawio.png) ## 🗃️ 效果图 以下是截取的是 pro 版的效果图展示:
## 📄 商用注意事项 此项目可免费用于商业用途,请遵守 MIT 协议并保留作者技术支持声明。
================================================ FILE: babel.config.js ================================================ module.exports = { presets: ['@vue/cli-plugin-babel/preset'], } ================================================ FILE: deploy.sh ================================================ #!/usr/bin/env bash set -e npm run build cd dist touch .nojekyll git init git add -A git commit -m 'deploy' git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-better-template.git" master:gh-pages start "https://gitee.com/chu1204505056/vue-admin-better-template/pages" cd - cd - rimraf dist exec /bin/bash ================================================ FILE: http/mock.http ================================================ ###/changeLog/getList###mockServer POST http://localhost:80/mock-server/changeLog/getList Content-Type: application/x-www-form-urlencoded ### mockServer ###/colorfulIcon/list### POST http://localhost:80/mock-server/colorfulIcon/list Content-Type: application/x-www-form-urlencoded ###mockServer ###/menu/navigate### POST http://localhost:80/mock-server/menu/navigate Content-Type: application/x-www-form-urlenmockServer ### ###/icon/list### POST http://localhost:80/mock-server/icon/mockServer Content-Type: application/x-www-form-urlencoded ### ###/face/list###mockServer POST http://localhost:80/mock-server/face/list Content-Type: application/x-www-form-urlencoded ### mockServer ###/table/list### POST http://localhost:80/mock-server/table/list Content-Type: application/x-www-form-urlencoded ###mockServer ###/remixicon/getList### POST http://localhost:80/mock-server/remixicon/getList Content-Type: application/x-www-form-urlenmockServer ### ###/publicKey### POST http://localhost:80/mock-server/pumockServer Content-Type: application/x-www-form-urlencoded ### ###/tree/list###mockServer POST http://localhost:80/mock-server/tree/list Content-Type: application/x-www-form-urlencoded ### mockServer ###/upload### POST http://localhost:80/mock-server/upload Content-Type: application/x-www-form-urlencoded ###mockServer ###/login### POST http://localhost:80/mock-server/login Content-Type: application/x-www-form-urlenmockServer ### ###/waterfall/list### POST http://localhost:80/mock-server/waterfall/list Content-Type: application/x-www-form-urlencoded ### ###/logout### POST http://localhost:80/mock-server/logout Content-Type: application/x-www-form-urlencoded ### ###/user/info### POST http://localhost:80/mock-server/user/info Content-Type: application/x-www-form-urlencoded ### ================================================ FILE: layouts/Permissions/index.js ================================================ 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 ================================================ FILE: layouts/Permissions/permissions.js ================================================ 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) } }, } ================================================ FILE: layouts/VabColorfullIcon/index.vue ================================================ ================================================ FILE: layouts/VabErrorLog/index.vue ================================================ ================================================ FILE: layouts/VabFullScreenBar/index.vue ================================================ ================================================ FILE: layouts/VabGithubCorner/index.vue ================================================ ================================================ FILE: layouts/VabQueryForm/VabQueryFormBottomPanel.vue ================================================ ================================================ FILE: layouts/VabQueryForm/VabQueryFormLeftPanel.vue ================================================ ================================================ FILE: layouts/VabQueryForm/VabQueryFormRightPanel.vue ================================================ ================================================ FILE: layouts/VabQueryForm/VabQueryFormTopPanel.vue ================================================ ================================================ FILE: layouts/VabQueryForm/index.vue ================================================ ================================================ FILE: layouts/VabRemixIcon/index.vue ================================================ ================================================ FILE: layouts/VabSideBar/components/VabMenuItem.vue ================================================ ================================================ FILE: layouts/VabSideBar/components/VabSideBarItem.vue ================================================ ================================================ FILE: layouts/VabSideBar/components/VabSubmenu.vue ================================================ ================================================ FILE: layouts/VabSideBar/index.vue ================================================ ================================================ FILE: layouts/VabTabsBar/index.vue ================================================ ================================================ FILE: layouts/VabTopBar/index.vue ================================================ ================================================ FILE: layouts/index.js ================================================ module.exports = { webpackBarName: 'vue-admin-better', webpackBanner: ' build: vue-admin-better \n vue-admin-better.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-better.com/admin-pro`) ) console.log( chalk.green(`> plus版演示地址:http://vue-admin-better.com/admin-plus`) ) 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') }, } ================================================ FILE: layouts/package.json ================================================ { "name": "layouts", "version": "1.0.0", "main": "index.js" } ================================================ FILE: layouts/prettier.config.js ================================================ 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', } ================================================ FILE: license.md ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: mock/controller/ad.js ================================================ const data = [ { title: 'vue-admin-better-pro 1.7版本已发布,点我提前体验', url: 'https://chu1204505056.gitee.io/vue-admin-better-pro/#/index', }, { title: 'vue-admin-better(antdv) vue3.0版本已发布,点我提前体验', url: 'https://chu1204505056.gitee.io/vue-admin-better-mini/#/index', }, ] module.exports = [ { url: '/ad/getList', type: 'get', response() { return { code: 200, msg: 'success', data, } }, }, ] ================================================ FILE: mock/controller/router.js ================================================ const data = [ { path: '/', component: 'Layout', redirect: 'index', children: [ { path: 'index', name: 'Index', component: '@/views/index/index', meta: { title: '首页', icon: 'home', affix: true, }, }, ], }, { 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: 'post', response() { return { code: 200, msg: 'success', data: data } }, }, ] ================================================ FILE: mock/controller/user.js ================================================ const accessTokens = { admin: 'admin-accessToken', editor: 'editor-accessToken', test: 'test-accessToken', } 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', response(config) { const { username } = config.body const accessToken = accessTokens[username] if (!accessToken) { return { code: 500, msg: '帐户或密码不正确。', } } return { code: 200, msg: 'success', data: { accessToken }, } }, }, { url: '/register', type: 'post', response() { return { code: 200, msg: '模拟注册成功', } }, }, { url: '/userInfo', type: 'post', response(config) { const { accessToken } = config.body let permissions = ['admin'] let username = 'admin' if ('admin-accessToken' === accessToken) { permissions = ['admin'] username = 'admin' } if ('editor-accessToken' === accessToken) { permissions = ['editor'] username = 'editor' } if ('test-accessToken' === accessToken) { permissions = ['admin', 'editor'] username = 'test' } return { code: 200, msg: 'success', data: { 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', ], }, } }, }, { url: '/logout', type: 'post', response() { return { code: 200, msg: 'success', } }, }, ] ================================================ FILE: mock/index.js ================================================ /** * @author chuzhixin 1204505056@qq.com * @description 导入所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。 */ const { handleMockArray } = require('./utils') const mocks = [] const mockArray = handleMockArray() mockArray.forEach((item) => { const obj = require(item) mocks.push(...obj) }) module.exports = { mocks, } ================================================ FILE: mock/mockServer.js ================================================ 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, { 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)) } } }) } ================================================ FILE: mock/utils/index.js ================================================ const { Random } = require('mockjs') const { join } = require('path') const fs = require('fs') /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 随机生成图片url。 * @param width * @param height * @returns {string} */ function handleRandomImage(width = 50, height = 50) { return `https://picsum.photos/${width}/${height}?random=${Random.guid()}` } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 处理所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。 * @returns {[]} */ function handleMockArray() { const mockArray = [] const getFiles = (jsonPath) => { const jsonFiles = [] const findJsonFile = (path) => { const files = fs.readdirSync(path) files.forEach((item) => { const fPath = join(path, item) const stat = fs.statSync(fPath) if (stat.isDirectory() === true) findJsonFile(item) if (stat.isFile() === true) jsonFiles.push(item) }) } findJsonFile(jsonPath) jsonFiles.forEach((item) => mockArray.push(`./controller/${item}`)) } getFiles('mock/controller') return mockArray } module.exports = { handleRandomImage, handleMockArray, } ================================================ FILE: package.json ================================================ { "name": "vue-admin-better-template", "version": "1.0.0", "private": true, "author": "vue-admin-better", "participants": [], "homepage": "https://chu1204505056.gitee.io/vue-admin-better", "scripts": { "serve": "vue-cli-service serve", "serve:node18": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve", "build": "vue-cli-service build", "build:node18": "set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service build", "lint": "vue-cli-service lint", "clear": "rimraf node_modules&&npm install --registry=--registry=https://registry.npmmirror.com", "image-webpack-loader": "cnpm i image-webpack-loader -D", "update": "ncu -u --reject layouts,sass-loader,sass,screenfull,eslint,chalk,vue-echarts,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&&cnpm i", "push": "start ./push.sh" }, "repository": { "type": "git", "url": "git+https://github.com/chuzhixin/vue-admin-better-template.git" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "src/**/*.{js,vue}": [ "eslint --fix", "git add" ] }, "dependencies": { "axios": "^0.21.1", "core-js": "^3.15.2", "dayjs": "^1.10.6", "element-ui": "^2.15.3", "js-cookie": "^3.0.0", "jsencrypt": "3.2.1", "lodash": "^4.17.21", "mockjs": "^1.1.0", "nprogress": "^0.2.0", "qs": "^6.10.1", "screenfull": "^5.1.0", "vab-icon": "file:vab-icon", "vue": "^2.6.14", "vue-router": "^3.5.2", "vuex": "^3.6.2", "layouts": "file:layouts" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.5.13", "@vue/cli-plugin-eslint": "^4.5.13", "@vue/cli-service": "^4.5.13", "@vue/eslint-config-prettier": "^6.0.0", "babel-eslint": "^10.1.0", "body-parser": "^1.19.0", "chalk": "^4.1.1", "chokidar": "^3.5.2", "eslint": "^7.31.0", "eslint-plugin-prettier": "^3.4.0", "eslint-plugin-vue": "^7.14.0", "filemanager-webpack-plugin": "^6.1.4", "image-webpack-loader": "^7.0.1", "lint-staged": "^11.1.1", "plop": "^2.7.4", "prettier": "^2.3.2", "sass": "^1.32.8", "sass-loader": "^10.1.1", "stylelint": "^13.13.1", "stylelint-config-prettier": "^8.0.2", "stylelint-config-recess-order": "^2.4.0", "svg-sprite-loader": "^6.0.9", "vue-template-compiler": "^2.6.14", "webpackbar": "^4.0.0" }, "engines": { "node": ">=8.9", "npm": ">= 3.0.0" } } ================================================ FILE: plopfile.js ================================================ const viewGenerator = require('zx-templates/view/prompt') const curdGenerator = require('zx-templates/curd/prompt') const componentGenerator = require('zx-templates/component/prompt') const mockGenerator = require('zx-templates/mock/prompt') const vuexGenerator = require('zx-templates/vuex/prompt') module.exports = (plop) => { plop.setGenerator('view', viewGenerator) plop.setGenerator('curd', curdGenerator) plop.setGenerator('component', componentGenerator) plop.setGenerator('mock&api', mockGenerator) plop.setGenerator('vuex', vuexGenerator) } ================================================ FILE: prettier.config.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 代码规范 */ 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', } ================================================ FILE: public/index.html ================================================ <%= VUE_APP_TITLE %>

<%= VUE_APP_TITLE %>

================================================ FILE: public/static/css/loading.css ================================================ .first-loading-wrp { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 90vh; min-height: 90vh; } .first-loading-wrp > h1 { font-size: 30px; font-weight: bolder; } .first-loading-wrp .loading-wrp { display: flex; align-items: center; justify-content: center; padding: 98px; } .dot { position: relative; box-sizing: border-box; display: inline-block; width: 64px; height: 64px; font-size: 64px; transform: rotate(45deg); animation: antRotate 1.2s infinite linear; } .dot i { position: absolute; display: block; width: 28px; height: 28px; background-color: #1890ff; border-radius: 100%; opacity: 0.3; transform: scale(0.75); transform-origin: 50% 50%; animation: antSpinMove 1s infinite linear alternate; } .dot i:nth-child(1) { top: 0; left: 0; } .dot i:nth-child(2) { top: 0; right: 0; -webkit-animation-delay: 0.4s; animation-delay: 0.4s; } .dot i:nth-child(3) { right: 0; bottom: 0; -webkit-animation-delay: 0.8s; animation-delay: 0.8s; } .dot i:nth-child(4) { bottom: 0; left: 0; -webkit-animation-delay: 1.2s; animation-delay: 1.2s; } @keyframes antRotate { to { -webkit-transform: rotate(405deg); transform: rotate(405deg); } } @-webkit-keyframes antRotate { to { -webkit-transform: rotate(405deg); transform: rotate(405deg); } } @keyframes antSpinMove { to { opacity: 1; } } @-webkit-keyframes antSpinMove { to { opacity: 1; } } ================================================ FILE: push.sh ================================================ #!/usr/bin/env bash set -e git init git add -A git commit -m 'deploy' git push -f "https://${access_token}@github.com/chuzhixin/vue-admin-better-template.git" master start "https://github.com/chuzhixin/vue-admin-better-template" exec /bin/bash ================================================ FILE: src/App.vue ================================================ ================================================ FILE: src/api/ad.js ================================================ 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, }) } ================================================ FILE: src/api/publicKey.js ================================================ import request from '@/utils/request' export function getPublicKey() { return request({ url: '/publicKey', method: 'post', }) } ================================================ FILE: src/api/router.js ================================================ import request from '@/utils/request' export function getRouterList(data) { return request({ url: '/menu/navigate', method: 'post', data, }) } ================================================ FILE: src/api/user.js ================================================ import request from '@/utils/request' 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', data, }) } export function getUserInfo(accessToken) { return request({ url: '/userInfo', method: 'post', data: { [tokenName]: accessToken, }, }) } export function logout() { return request({ url: '/logout', method: 'post', }) } export function register() { return request({ url: '/register', method: 'post', }) } ================================================ FILE: src/colorfulIcon/index.js ================================================ 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) ================================================ FILE: src/config/index.js ================================================ /** * @description 3个子配置,通用配置|主题配置|网络配置导出 */ const setting = require('./setting.config') const theme = require('./theme.config') const network = require('./net.config') module.exports = Object.assign({}, setting, theme, network) ================================================ FILE: src/config/net.config.js ================================================ /** * @description 导出默认网路配置 **/ const network = { //配后端数据的接收方式application/json;charset=UTF-8或者application/x-www-form-urlencoded;charset=UTF-8 contentType: 'application/json;charset=UTF-8', //消息框消失时间 messageDuration: 3000, //最长请求时间 requestTimeout: 5000, //操作正常code,支持String、Array、int多种类型 successCode: [200, 0], //登录失效code invalidCode: 402, //无权限code noPermissionCode: 401, } module.exports = network ================================================ FILE: src/config/permission.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 路由守卫,目前两种模式:all模式与intelligence模式 */ import router from '@/router' import store from '@/store' import VabProgress from 'nprogress' import 'nprogress/nprogress.css' import getPageTitle from '@/utils/pageTitle' import { authentication, loginInterception, progressBar, recordRoute, routesWhiteList, } from '@/config' VabProgress.configure({ easing: 'ease', speed: 500, trickleSpeed: 200, showSpinner: false, }) router.beforeResolve(async (to, from, next) => { if (progressBar) VabProgress.start() let hasToken = store.getters['user/accessToken'] if (!loginInterception) hasToken = true if (hasToken) { if (to.path === '/login') { next({ path: '/' }) if (progressBar) VabProgress.done() } else { const hasPermissions = store.getters['user/permissions'] && store.getters['user/permissions'].length > 0 if (hasPermissions) { next() } else { try { let permissions if (!loginInterception) { //settings.js loginInterception为false时,创建虚拟权限 await store.dispatch('user/setPermissions', ['admin']) permissions = ['admin'] } else { permissions = await store.dispatch('user/getUserInfo') } let accessRoutes = [] if (authentication === 'intelligence') { accessRoutes = await store.dispatch('routes/setRoutes', permissions) } else if (authentication === 'all') { accessRoutes = await store.dispatch('routes/setAllRoutes') } router.addRoutes(accessRoutes) next({ ...to, replace: true }) } catch { await store.dispatch('user/resetAccessToken') if (progressBar) VabProgress.done() } } } } else { if (routesWhiteList.indexOf(to.path) !== -1) { next() } else { if (recordRoute) { next(`/login?redirect=${to.path}`) } else { next('/login') } if (progressBar) VabProgress.done() } } document.title = getPageTitle(to.meta.title) }) router.afterEach(() => { if (progressBar) VabProgress.done() }) ================================================ FILE: src/config/setting.config.js ================================================ /** * @description 导出默认通用配置 */ const setting = { // 开发以及部署时的URL publicPath: '', // 生产环境构建文件的目录名 outputDir: 'dist', // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。 assetsDir: 'static', // 开发环境每次保存时是否输出为eslint编译警告 lintOnSave: true, // 进行编译的依赖 transpileDependencies: ['vue-echarts', 'resize-detector'], // 默认的接口地址 如果是开发环境和生产环境走vab-mock-server,当然你也可以选择自己配置成需要的接口地址 baseURL: process.env.NODE_ENV === 'development' ? 'vab-mock-server' : 'vab-mock-server', //标题 (包括初次加载雪花屏的标题 页面的标题 浏览器的标题) title: 'vue-admin-better', //简写 abbreviation: 'vab', //开发环境端口号 devPort: '82', //版本号 version: process.env.VUE_APP_VERSION, //这一项非常重要!请务必保留MIT协议下package.json及copyright作者信息 即可免费商用,不遵守此项约定你将无法使用该框架,如需自定义版权信息请联系QQ1204505056 copyright: 'vab', //是否显示页面底部自定义版权信息 footerCopyright: true, //是否显示顶部进度条 progressBar: true, //缓存路由的最大数量 keepAliveMaxNum: 99, // 路由模式,可选值为 history 或 hash routerMode: 'hash', //不经过token校验的路由 routesWhiteList: ['/login', '/register', '/404', '/401'], //加载时显示文字 loadingText: '正在加载中...', //token名称 tokenName: 'accessToken', //token在localStorage、sessionStorage存储的key的名称 tokenTableName: 'vue-admin-better', //token存储位置localStorage sessionStorage storage: 'localStorage', //token失效回退到登录页时是否记录本次的路由 recordRoute: true, //是否显示logo,不显示时设置false,显示时请填写remixIcon图标名称,暂时只支持设置remixIcon logo: 'vuejs-fill', //是否显示在页面高亮错误 errorLog: ['development'], //是否开启登录拦截 loginInterception: true, //是否开启登录RSA加密 loginRSA: false, //intelligence和all两种方式,前者后端权限只控制permissions不控制view文件的import(前后端配合,减轻后端工作量),all方式完全交给后端前端只负责加载 authentication: 'intelligence', //vertical布局时是否只保持一个子菜单的展开 uniqueOpened: true, //vertical布局时默认展开的菜单path,使用逗号隔开建议只展开一个 defaultOopeneds: ['/vab'], //需要加loading层的请求,防止重复提交 debounce: ['doEdit'], //需要自动注入并加载的模块 providePlugin: { maptalks: 'maptalks', 'window.maptalks': 'maptalks' }, //npm run build时是否自动生成7z压缩包 build7z: false, //代码生成机生成在view下的文件夹名称 templateFolder: 'project', //是否显示终端donation打印 donation: true, } module.exports = setting ================================================ FILE: src/config/settings.js ================================================ /** * @description 3个子配置,通用配置|主题配置|网络配置 */ //默认配置 const { setting, theme, network } = require('./') module.exports = Object.assign({}, setting, theme, network) ================================================ FILE: src/config/theme.config.js ================================================ /** * @description 导出默认主题配置 */ const theme = { //是否国定头部 固定fixed 不固定noFixed header: 'fixed', //横纵布局 horizontal vertical layout: 'vertical', //是否开启主题配置按钮 themeBar: true, //是否显示多标签页 tabsBar: true, } module.exports = theme ================================================ FILE: src/layouts/EmptyLayout.vue ================================================ ================================================ FILE: src/layouts/components/VabAd/index.vue ================================================ ================================================ FILE: src/layouts/components/VabAppMain/index.vue ================================================ ================================================ FILE: src/layouts/components/VabAvatar/index.vue ================================================ ================================================ FILE: src/layouts/components/VabBreadcrumb/index.vue ================================================ ================================================ FILE: src/layouts/components/VabLogo/index.vue ================================================ ================================================ FILE: src/layouts/components/VabNavBar/index.vue ================================================ ================================================ FILE: src/layouts/components/VabThemeBar/index.vue ================================================ ================================================ FILE: src/layouts/export.js ================================================ /** * @author https://vue-admin-better.com (不想保留author可删除) * @description 公共布局及样式自动引入 */ import Vue from 'vue' const requireComponents = require.context('./components', true, /\.vue$/) requireComponents.keys().forEach((fileName) => { const componentConfig = requireComponents(fileName) const componentName = componentConfig.default.name Vue.component(componentName, componentConfig.default || componentConfig) }) const requireZxLayouts = require.context('layouts', true, /\.vue$/) requireZxLayouts.keys().forEach((fileName) => { const componentConfig = requireZxLayouts(fileName) const componentName = componentConfig.default.name Vue.component(componentName, componentConfig.default || componentConfig) }) const requireThemes = require.context('@/styles/themes', true, /\.scss$/) requireThemes.keys().forEach((fileName) => { require(`@/styles/themes/${fileName.slice(2)}`) }) ================================================ FILE: src/layouts/index.vue ================================================ ================================================ FILE: src/main.js ================================================ import Vue from 'vue' import App from './App' import store from './store' import router from './router' import './plugins' import '@/layouts/export' /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 生产环境默认都使用mock,如果正式用于生产环境时,记得去掉 */ if (process.env.NODE_ENV === 'production') { const { mockXHR } = require('@/utils/static') mockXHR() } Vue.config.productionTip = false new Vue({ el: '#vue-admin-better', router, store, render: (h) => h(App), }) ================================================ FILE: src/plugins/element.js ================================================ import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/display.css' import '@/styles/element-variables.scss' Vue.use(ElementUI, { size: 'small', }) ================================================ FILE: src/plugins/index.js ================================================ /* 公共引入,勿随意修改,修改时需经过确认 */ import Vue from 'vue' import './element' import './support' import '@/styles/vab.scss' import '@/remixIcon' import '@/colorfulIcon' import '@/config/permission' import '@/utils/errorLog' import './vabIcon' import Vab from '@/utils/vab' import VabPermissions from 'layouts/Permissions' Vue.use(Vab) Vue.use(VabPermissions) ================================================ FILE: src/plugins/support.js ================================================ import { MessageBox } from 'element-ui' import { donation } from '@/config' import { dependencies, repository } from '../../package.json' if (!!window.ActiveXObject || 'ActiveXObject' in window) { MessageBox({ title: '温馨提示', message: '自2015年3月起,微软已宣布弃用IE,且不再对IE提供任何更新维护,请点击此处访问微软官网更新浏览器,如果您使用的是双核浏览器,请您切换浏览器内核为极速模式', type: 'warning', showClose: false, showConfirmButton: false, closeOnClickModal: false, closeOnPressEscape: false, closeOnHashChange: false, dangerouslyUseHTMLString: true, }) } if (!dependencies['vab-icon'] || !dependencies['layouts']) document.body.innerHTML = '' ================================================ FILE: src/plugins/vabIcon.js ================================================ import Vue from 'vue' import VabIcon from 'vab-icon' Vue.component('VabIcon', VabIcon) ================================================ FILE: src/remixIcon/index.js ================================================ 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) ================================================ FILE: src/router/index.js ================================================ /** * @copyright chuzhixin 1204505056@qq.com * @description router全局配置,如有必要可分文件抽离 */ import Vue from 'vue' import VueRouter from 'vue-router' import Layout from '@/layouts' import EmptyLayout from '@/layouts/EmptyLayout' import { publicPath, routerMode } from '@/config' Vue.use(VueRouter) export const constantRoutes = [ { path: '/login', component: () => import('@/views/login/index'), hidden: true, }, { path: '/register', component: () => import('@/views/register/index'), hidden: true, }, { path: '/401', name: '401', component: () => import('@/views/401'), hidden: true, }, { path: '/404', name: '404', component: () => import('@/views/404'), hidden: true, }, ] /*当settings.js里authentication配置的是intelligence时,views引入交给前端配置*/ export const asyncRoutes = [ { path: '/', component: Layout, redirect: '/index', children: [ { path: '/index', name: 'Index', component: () => import('@/views/index/index'), meta: { title: '首页', icon: 'home', affix: true, noKeepAlive: true, }, }, ], }, { path: '*', redirect: '/404', hidden: true, }, ] const router = new VueRouter({ base: routerMode === 'history' ? publicPath : '', mode: routerMode, scrollBehavior: () => ({ y: 0, }), routes: constantRoutes, }) //注释的地方是允许路由重复点击,如果你觉得框架路由跳转规范太过严格可选择放开 /* const originalPush = VueRouter.prototype.push; VueRouter.prototype.push = function push(location, onResolve, onReject) { if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject); return originalPush.call(this, location).catch((err) => err); }; */ export function resetRouter() { router.matcher = new VueRouter({ base: routerMode === 'history' ? publicPath : '', mode: routerMode, scrollBehavior: () => ({ y: 0, }), routes: constantRoutes, }).matcher } export default router ================================================ FILE: src/store/index.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 导入所有 vuex 模块,自动加入namespaced:true,用于解决vuex命名冲突,请勿修改。 */ import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) const files = require.context('./modules', false, /\.js$/) const modules = {} files.keys().forEach((key) => { modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default }) Object.keys(modules).forEach((key) => { modules[key]['namespaced'] = true }) const store = new Vuex.Store({ modules, }) export default store ================================================ FILE: src/store/modules/errorLog.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 异常捕获的状态拦截,请勿修改 */ const state = { errorLogs: [] } const getters = { errorLogs: (state) => state.errorLogs, } const mutations = { addErrorLog(state, errorLog) { state.errorLogs.push(errorLog) }, clearErrorLog: (state) => { state.errorLogs.splice(0) }, } const actions = { addErrorLog({ commit }, errorLog) { commit('addErrorLog', errorLog) }, clearErrorLog({ commit }) { commit('clearErrorLog') }, } export default { state, getters, mutations, actions } ================================================ FILE: src/store/modules/routes.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 路由拦截状态管理,目前两种模式:all模式与intelligence模式,其中partialRoutes是菜单暂未使用 */ import { asyncRoutes, constantRoutes } from '@/router' import { getRouterList } from '@/api/router' import { convertRouter, filterAsyncRoutes } from '@/utils/handleRoutes' const state = { routes: [], partialRoutes: [] } const getters = { routes: (state) => state.routes, partialRoutes: (state) => state.partialRoutes, } const mutations = { setRoutes(state, routes) { state.routes = constantRoutes.concat(routes) }, setAllRoutes(state, routes) { state.routes = constantRoutes.concat(routes) }, setPartialRoutes(state, routes) { state.partialRoutes = constantRoutes.concat(routes) }, } const actions = { async setRoutes({ commit }, permissions) { //开源版只过滤动态路由permissions,admin不再默认拥有全部权限 const finallyAsyncRoutes = await filterAsyncRoutes( [...asyncRoutes], permissions ) commit('setRoutes', finallyAsyncRoutes) return finallyAsyncRoutes }, async setAllRoutes({ commit }) { let { data } = await getRouterList() data.push({ path: '*', redirect: '/404', hidden: true }) let accessRoutes = convertRouter(data) commit('setAllRoutes', accessRoutes) return accessRoutes }, setPartialRoutes({ commit }, accessRoutes) { commit('setPartialRoutes', accessRoutes) return accessRoutes }, } export default { state, getters, mutations, actions } ================================================ FILE: src/store/modules/settings.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 所有全局配置的状态管理,如无必要请勿修改 */ import defaultSettings from '@/config' const { tabsBar, logo, layout, header, themeBar } = defaultSettings const theme = JSON.parse(localStorage.getItem('vue-admin-better-theme')) || '' const state = { tabsBar: theme.tabsBar || tabsBar, logo, collapse: false, layout: theme.layout || layout, header: theme.header || header, device: 'desktop', themeBar, } const getters = { collapse: (state) => state.collapse, device: (state) => state.device, header: (state) => state.header, layout: (state) => state.layout, logo: (state) => state.logo, tabsBar: (state) => state.tabsBar, themeBar: (state) => state.themeBar, } const mutations = { changeLayout: (state, layout) => { if (layout) state.layout = layout }, changeHeader: (state, header) => { if (header) state.header = header }, changeTabsBar: (state, tabsBar) => { if (tabsBar) state.tabsBar = tabsBar }, changeCollapse: (state) => { state.collapse = !state.collapse }, foldSideBar: (state) => { state.collapse = true }, openSideBar: (state) => { state.collapse = false }, toggleDevice: (state, device) => { state.device = device }, } const actions = { changeLayout({ commit }, layout) { commit('changeLayout', layout) }, changeHeader({ commit }, header) { commit('changeHeader', header) }, changeTabsBar({ commit }, tabsBar) { commit('changeTabsBar', tabsBar) }, changeCollapse({ commit }) { commit('changeCollapse') }, foldSideBar({ commit }) { commit('foldSideBar') }, openSideBar({ commit }) { commit('openSideBar') }, toggleDevice({ commit }, device) { commit('toggleDevice', device) }, } export default { state, getters, mutations, actions } ================================================ FILE: src/store/modules/table.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 代码生成机状态管理 */ const state = { srcCode: '' } const getters = { srcTableCode: (state) => state.srcCode, } const mutations = { setTableCode(state, srcCode) { state.srcCode = srcCode }, } const actions = { setTableCode({ commit }, srcCode) { commit('setTableCode', srcCode) }, } export default { state, getters, mutations, actions } ================================================ FILE: src/store/modules/tabsBar.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description tabsBar多标签页逻辑,前期借鉴了很多开源项目发现都有个共同的特点很繁琐并不符合框架设计的初衷,后来在github用户cyea的启发下完成了重构,请勿修改 */ const state = { visitedRoutes: [], } const getters = { visitedRoutes: (state) => state.visitedRoutes, } const mutations = { addVisitedRoute(state, route) { let target = state.visitedRoutes.find((item) => item.path === route.path) if (target) { if (route.fullPath !== target.fullPath) Object.assign(target, route) return } state.visitedRoutes.push(Object.assign({}, route)) }, delVisitedRoute(state, route) { state.visitedRoutes.forEach((item, index) => { if (item.path === route.path) state.visitedRoutes.splice(index, 1) }) }, delOthersVisitedRoute(state, route) { state.visitedRoutes = state.visitedRoutes.filter( (item) => item.meta.affix || item.path === route.path ) }, delLeftVisitedRoute(state, route) { let index = state.visitedRoutes.length state.visitedRoutes = state.visitedRoutes.filter((item) => { if (item.name === route.name) index = state.visitedRoutes.indexOf(item) return item.meta.affix || index <= state.visitedRoutes.indexOf(item) }) }, delRightVisitedRoute(state, route) { let index = state.visitedRoutes.length state.visitedRoutes = state.visitedRoutes.filter((item) => { if (item.name === route.name) index = state.visitedRoutes.indexOf(item) return item.meta.affix || index >= state.visitedRoutes.indexOf(item) }) }, delAllVisitedRoutes(state) { state.visitedRoutes = state.visitedRoutes.filter((item) => item.meta.affix) }, updateVisitedRoute(state, route) { state.visitedRoutes.forEach((item) => { if (item.path === route.path) item = Object.assign(item, route) }) }, } const actions = { addVisitedRoute({ commit }, route) { commit('addVisitedRoute', route) }, async delRoute({ dispatch, state }, route) { await dispatch('delVisitedRoute', route) return { visitedRoutes: [...state.visitedRoutes], } }, delVisitedRoute({ commit, state }, route) { commit('delVisitedRoute', route) return [...state.visitedRoutes] }, async delOthersRoutes({ dispatch, state }, route) { await dispatch('delOthersVisitedRoute', route) return { visitedRoutes: [...state.visitedRoutes], } }, async delLeftRoutes({ dispatch, state }, route) { await dispatch('delLeftVisitedRoute', route) return { visitedRoutes: [...state.visitedRoutes], } }, async delRightRoutes({ dispatch, state }, route) { await dispatch('delRightVisitedRoute', route) return { visitedRoutes: [...state.visitedRoutes], } }, delOthersVisitedRoute({ commit, state }, route) { commit('delOthersVisitedRoute', route) return [...state.visitedRoutes] }, delLeftVisitedRoute({ commit, state }, route) { commit('delLeftVisitedRoute', route) return [...state.visitedRoutes] }, delRightVisitedRoute({ commit, state }, route) { commit('delRightVisitedRoute', route) return [...state.visitedRoutes] }, async delAllRoutes({ dispatch, state }, route) { await dispatch('delAllVisitedRoutes', route) return { visitedRoutes: [...state.visitedRoutes], } }, delAllVisitedRoutes({ commit, state }) { commit('delAllVisitedRoutes') return [...state.visitedRoutes] }, updateVisitedRoute({ commit }, route) { commit('updateVisitedRoute', route) }, } export default { state, getters, mutations, actions } ================================================ FILE: src/store/modules/user.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 登录、获取用户信息、退出登录、清除accessToken逻辑,不建议修改 */ import Vue from 'vue' import { getUserInfo, login, logout } from '@/api/user' import { getAccessToken, removeAccessToken, setAccessToken, } from '@/utils/accessToken' import { resetRouter } from '@/router' import { title, tokenName } from '@/config' const state = { accessToken: getAccessToken(), username: '', avatar: '', permissions: [], } const getters = { accessToken: (state) => state.accessToken, username: (state) => state.username, avatar: (state) => state.avatar, permissions: (state) => state.permissions, } const mutations = { setAccessToken(state, accessToken) { state.accessToken = accessToken setAccessToken(accessToken) }, setUsername(state, username) { state.username = username }, setAvatar(state, avatar) { state.avatar = avatar }, setPermissions(state, permissions) { state.permissions = permissions }, } const actions = { setPermissions({ commit }, permissions) { commit('setPermissions', permissions) }, async login({ commit }, userInfo) { const { data } = await login(userInfo) const accessToken = data[tokenName] if (accessToken) { commit('setAccessToken', accessToken) const hour = new Date().getHours() const thisTime = hour < 8 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 18 ? '下午好' : '晚上好' Vue.prototype.$baseNotify(`欢迎登录${title}`, `${thisTime}!`) } else { Vue.prototype.$baseMessage( `登录接口异常,未正确返回${tokenName}...`, 'error' ) } }, async getUserInfo({ commit, state }) { const { data } = await getUserInfo(state.accessToken) if (!data) { Vue.prototype.$baseMessage('验证失败,请重新登录...', 'error') return false } let { permissions, username, avatar } = data if (permissions && username && Array.isArray(permissions)) { commit('setPermissions', permissions) commit('setUsername', username) commit('setAvatar', avatar) return permissions } else { Vue.prototype.$baseMessage('用户信息接口异常', 'error') return false } }, async logout({ dispatch }) { await logout(state.accessToken) await dispatch('resetAccessToken') await resetRouter() }, resetAccessToken({ commit }) { commit('setPermissions', []) commit('setAccessToken', '') removeAccessToken() }, } export default { state, getters, mutations, actions } ================================================ FILE: src/styles/element-variables.scss ================================================ @charset "utf-8"; /* Transition -------------------------- */ $--all-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); $--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); $--fade-linear-transition: opacity 200ms linear; $--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); $--border-transition-base: border-color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); $--color-transition-base: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); /* Color -------------------------- */ /// color|1|Brand Color|0 $--color-primary: $base-color-blue; /// color|1|Background Color|4 $--color-white: #fff; /// color|1|Background Color|4 $--color-black: #000; $--color-primary-light-1: mix($--color-white, $--color-primary, 10%); /* 53a8ff */ $--color-primary-light-2: mix($--color-white, $--color-primary, 20%); /* 66b1ff */ $--color-primary-light-3: mix($--color-white, $--color-primary, 30%); /* 79bbff */ $--color-primary-light-4: mix($--color-white, $--color-primary, 40%); /* 8cc5ff */ $--color-primary-light-5: mix($--color-white, $--color-primary, 50%); /* a0cfff */ $--color-primary-light-6: mix($--color-white, $--color-primary, 60%); /* b3d8ff */ $--color-primary-light-7: mix($--color-white, $--color-primary, 70%); /* c6e2ff */ $--color-primary-light-8: mix($--color-white, $--color-primary, 80%); /* d9ecff */ $--color-primary-light-9: mix($--color-white, $--color-primary, 90%); /* ecf5ff */ /// color|1|Functional Color|1 $--color-success: $base-color-green; /// color|1|Functional Color|1 $--color-warning: $base-color-yellow; /// color|1|Functional Color|1 $--color-danger: $base-color-red; /// color|1|Functional Color|1 $--color-info: #909399; $--color-success-light: mix($--color-white, $--color-success, 80%); $--color-warning-light: mix($--color-white, $--color-warning, 80%); $--color-danger-light: mix($--color-white, $--color-danger, 80%); $--color-info-light: mix($--color-white, $--color-info, 80%); $--color-success-lighter: mix($--color-white, $--color-success, 90%); $--color-warning-lighter: mix($--color-white, $--color-warning, 90%); $--color-danger-lighter: mix($--color-white, $--color-danger, 90%); $--color-info-lighter: mix($--color-white, $--color-info, 90%); /// color|1|Font Color|2 $--color-text-primary: #303133; /// color|1|Font Color|2 $--color-text-regular: #606266; /// color|1|Font Color|2 $--color-text-secondary: #909399; /// color|1|Font Color|2 $--color-text-placeholder: #c0c4cc; /// color|1|Border Color|3 $--border-color-base: #dcdfe6; /// color|1|Border Color|3 $--border-color-light: #e4e7ed; /// color|1|Border Color|3 $--border-color-lighter: #ebeef5; /// color|1|Border Color|3 $--border-color-extra-light: #f2f6fc; // Background /// color|1|Background Color|4 $--background-color-base: #f5f7fa; /* Link -------------------------- */ $--link-color: $--color-primary-light-2; $--link-hover-color: $--color-primary; /* Border -------------------------- */ $--border-width-base: 1px; $--border-style-base: solid; $--border-color-hover: $--color-text-placeholder; $--border-base: $--border-width-base $--border-style-base $--border-color-base; /// borderRadius|1|Radius|0 $--border-radius-base: $base-border-radius; /// borderRadius|1|Radius|0 $--border-radius-small: $base-border-radius; /// borderRadius|1|Radius|0 $--border-radius-circle: 100%; /// borderRadius|1|Radius|0 $--border-radius-zero: 0; // Box-shadow /// boxShadow|1|Shadow|1 $--box-shadow-base: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04); // boxShadow|1|Shadow|1 $--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.12); /// boxShadow|1|Shadow|1 $--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1); /* Fill -------------------------- */ $--fill-base: $--color-white; /* Typography -------------------------- */ $--font-path: "~element-ui/lib/theme-chalk/fonts"; $--font-display: "auto"; /// fontSize|1|Font Size|0 $--font-size-extra-large: 20px; /// fontSize|1|Font Size|0 $--font-size-large: 18px; /// fontSize|1|Font Size|0 $--font-size-medium: 16px; /// fontSize|1|Font Size|0 $--font-size-base: 14px; /// fontSize|1|Font Size|0 $--font-size-small: 13px; /// fontSize|1|Font Size|0 $--font-size-extra-small: 12px; /// fontWeight|1|Font Weight|1 $--font-weight-primary: 500; /// fontWeight|1|Font Weight|1 $--font-weight-secondary: 100; /// fontLineHeight|1|Line Height|2 $--font-line-height-primary: 24px; /// fontLineHeight|1|Line Height|2 $--font-line-height-secondary: 16px; $--font-color-disabled-base: #bbb; /* Size -------------------------- */ $--size-base: 14px; /* z-index -------------------------- */ $--index-normal: 1; $--index-top: 1000; $--index-popper: 2000; /* Disable base -------------------------- */ $--disabled-fill-base: $--background-color-base; $--disabled-color-base: $--color-text-placeholder; $--disabled-border-base: $--border-color-light; /* Icon -------------------------- */ $--icon-color: #666; $--icon-color-base: $--color-info; /* Checkbox -------------------------- */ /// fontSize||Font|1 $--checkbox-font-size: 14px; /// fontWeight||Font|1 $--checkbox-font-weight: $--font-weight-primary; /// color||Color|0 $--checkbox-font-color: $--color-text-regular; $--checkbox-input-height: 14px; $--checkbox-input-width: 14px; /// borderRadius||Border|2 $--checkbox-border-radius: $--border-radius-small; /// color||Color|0 $--checkbox-background-color: $--color-white; $--checkbox-input-border: $--border-base; /// color||Color|0 $--checkbox-disabled-border-color: $--border-color-base; $--checkbox-disabled-input-fill: #edf2fc; $--checkbox-disabled-icon-color: $--color-text-placeholder; $--checkbox-disabled-checked-input-fill: $--border-color-extra-light; $--checkbox-disabled-checked-input-border-color: $--border-color-base; $--checkbox-disabled-checked-icon-color: $--color-text-placeholder; /// color||Color|0 $--checkbox-checked-font-color: $--color-primary; $--checkbox-checked-input-border-color: $--color-primary; /// color||Color|0 $--checkbox-checked-background-color: $--color-primary; $--checkbox-checked-icon-color: $--fill-base; $--checkbox-input-border-color-hover: $--color-primary; /// height||Other|4 $--checkbox-bordered-height: 40px; /// padding||Spacing|3 $--checkbox-bordered-padding: 9px 20px 9px 10px; /// padding||Spacing|3 $--checkbox-bordered-medium-padding: 7px 20px 7px 10px; /// padding||Spacing|3 $--checkbox-bordered-small-padding: 5px 15px 5px 10px; /// padding||Spacing|3 $--checkbox-bordered-mini-padding: 3px 15px 3px 10px; $--checkbox-bordered-medium-input-height: 14px; $--checkbox-bordered-medium-input-width: 14px; /// height||Other|4 $--checkbox-bordered-medium-height: 36px; $--checkbox-bordered-small-input-height: 12px; $--checkbox-bordered-small-input-width: 12px; /// height||Other|4 $--checkbox-bordered-small-height: 32px; $--checkbox-bordered-mini-input-height: 12px; $--checkbox-bordered-mini-input-width: 12px; /// height||Other|4 $--checkbox-bordered-mini-height: 28px; /// color||Color|0 $--checkbox-button-checked-background-color: $--color-primary; /// color||Color|0 $--checkbox-button-checked-font-color: $--color-white; /// color||Color|0 $--checkbox-button-checked-border-color: $--color-primary; /* Radio -------------------------- */ /// fontSize||Font|1 $--radio-font-size: $--font-size-base; /// fontWeight||Font|1 $--radio-font-weight: $--font-weight-primary; /// color||Color|0 $--radio-font-color: $--color-text-regular; $--radio-input-height: 14px; $--radio-input-width: 14px; /// borderRadius||Border|2 $--radio-input-border-radius: $--border-radius-circle; /// color||Color|0 $--radio-input-background-color: $--color-white; $--radio-input-border: $--border-base; /// color||Color|0 $--radio-input-border-color: $--border-color-base; /// color||Color|0 $--radio-icon-color: $--color-white; $--radio-disabled-input-border-color: $--disabled-border-base; $--radio-disabled-input-fill: $--disabled-fill-base; $--radio-disabled-icon-color: $--disabled-fill-base; $--radio-disabled-checked-input-border-color: $--disabled-border-base; $--radio-disabled-checked-input-fill: $--disabled-fill-base; $--radio-disabled-checked-icon-color: $--color-text-placeholder; /// color||Color|0 $--radio-checked-font-color: $--color-primary; /// color||Color|0 $--radio-checked-input-border-color: $--color-primary; /// color||Color|0 $--radio-checked-input-background-color: $--color-white; /// color||Color|0 $--radio-checked-icon-color: $--color-primary; $--radio-input-border-color-hover: $--color-primary; $--radio-bordered-height: 40px; $--radio-bordered-padding: 12px 20px 0 10px; $--radio-bordered-medium-padding: 10px 20px 0 10px; $--radio-bordered-small-padding: 8px 15px 0 10px; $--radio-bordered-mini-padding: 6px 15px 0 10px; $--radio-bordered-medium-input-height: 14px; $--radio-bordered-medium-input-width: 14px; $--radio-bordered-medium-height: 36px; $--radio-bordered-small-input-height: 12px; $--radio-bordered-small-input-width: 12px; $--radio-bordered-small-height: 32px; $--radio-bordered-mini-input-height: 12px; $--radio-bordered-mini-input-width: 12px; $--radio-bordered-mini-height: 28px; /// fontSize||Font|1 $--radio-button-font-size: $--font-size-base; /// color||Color|0 $--radio-button-checked-background-color: $--color-primary; /// color||Color|0 $--radio-button-checked-font-color: $--color-white; /// color||Color|0 $--radio-button-checked-border-color: $--color-primary; $--radio-button-disabled-checked-fill: $--border-color-extra-light; /* Select -------------------------- */ $--select-border-color-hover: $--border-color-hover; $--select-disabled-border: $--disabled-border-base; /// fontSize||Font|1 $--select-font-size: $--font-size-base; $--select-close-hover-color: $--color-text-secondary; $--select-input-color: $--color-text-placeholder; $--select-multiple-input-color: #666; /// color||Color|0 $--select-input-focus-border-color: $--color-primary; /// fontSize||Font|1 $--select-input-font-size: 14px; $--select-option-color: $--color-text-regular; $--select-option-disabled-color: $--color-text-placeholder; $--select-option-disabled-background: $--color-white; /// height||Other|4 $--select-option-height: 34px; $--select-option-hover-background: $--background-color-base; /// color||Color|0 $--select-option-selected-font-color: $--color-primary; $--select-option-selected-hover: $--background-color-base; $--select-group-color: $--color-info; $--select-group-height: 30px; $--select-group-font-size: 12px; $--select-dropdown-background: $--color-white; $--select-dropdown-shadow: $--box-shadow-light; $--select-dropdown-empty-color: #999; /// height||Other|4 $--select-dropdown-max-height: 274px; $--select-dropdown-padding: 6px 0; $--select-dropdown-empty-padding: 10px 0; $--select-dropdown-border: solid 1px $--border-color-light; /* Alert -------------------------- */ $--alert-padding: 8px 16px; /// borderRadius||Border|2 $--alert-border-radius: $--border-radius-base; /// fontSize||Font|1 $--alert-title-font-size: 13px; /// fontSize||Font|1 $--alert-description-font-size: 12px; /// fontSize||Font|1 $--alert-close-font-size: 12px; /// fontSize||Font|1 $--alert-close-customed-font-size: 13px; $--alert-success-color: $--color-success-lighter; $--alert-info-color: $--color-info-lighter; $--alert-warning-color: $--color-warning-lighter; $--alert-danger-color: $--color-danger-lighter; /// height||Other|4 $--alert-icon-size: 16px; /// height||Other|4 $--alert-icon-large-size: 28px; /* MessageBox -------------------------- */ /// color||Color|0 $--messagebox-title-color: $--color-text-primary; $--msgbox-width: 420px; $--msgbox-border-radius: $--border-radius-base; /// fontSize||Font|1 $--messagebox-font-size: $--font-size-large; /// fontSize||Font|1 $--messagebox-content-font-size: $--font-size-base; /// color||Color|0 $--messagebox-content-color: $--color-text-regular; /// fontSize||Font|1 $--messagebox-error-font-size: 12px; $--msgbox-padding-primary: 15px; /// color||Color|0 $--messagebox-success-color: $--color-success; /// color||Color|0 $--messagebox-info-color: $--color-info; /// color||Color|0 $--messagebox-warning-color: $--color-warning; /// color||Color|0 $--messagebox-danger-color: $--color-danger; /* Message -------------------------- */ $--message-shadow: $--box-shadow-base; $--message-min-width: 380px; $--message-background-color: #edf2fc; $--message-padding: 15px 15px 15px 20px; /// color||Color|0 $--message-close-icon-color: $--color-text-placeholder; /// height||Other|4 $--message-close-size: 16px; /// color||Color|0 $--message-close-hover-color: $--color-text-secondary; /// color||Color|0 $--message-success-font-color: $--color-success; /// color||Color|0 $--message-info-font-color: $--color-info; /// color||Color|0 $--message-warning-font-color: $--color-warning; /// color||Color|0 $--message-danger-font-color: $--color-danger; /* Notification -------------------------- */ $--notification-width: 330px; /// padding||Spacing|3 $--notification-padding: 14px 26px 14px 13px; $--notification-radius: 8px; $--notification-shadow: $--box-shadow-light; /// color||Color|0 $--notification-border-color: $--border-color-lighter; $--notification-icon-size: 24px; $--notification-close-font-size: $--message-close-size; $--notification-group-margin-left: 13px; $--notification-group-margin-right: 8px; /// fontSize||Font|1 $--notification-content-font-size: $--font-size-base; /// color||Color|0 $--notification-content-color: $--color-text-regular; /// fontSize||Font|1 $--notification-title-font-size: 16px; /// color||Color|0 $--notification-title-color: $--color-text-primary; /// color||Color|0 $--notification-close-color: $--color-text-secondary; /// color||Color|0 $--notification-close-hover-color: $--color-text-regular; /// color||Color|0 $--notification-success-icon-color: $--color-success; /// color||Color|0 $--notification-info-icon-color: $--color-info; /// color||Color|0 $--notification-warning-icon-color: $--color-warning; /// color||Color|0 $--notification-danger-icon-color: $--color-danger; /* Input -------------------------- */ $--input-font-size: $--font-size-base; /// color||Color|0 $--input-font-color: $--color-text-regular; /// height||Other|4 $--input-width: 140px; /// height||Other|4 $--input-height: 40px; $--input-border: $--border-base; $--input-border-color: $--border-color-base; /// borderRadius||Border|2 $--input-border-radius: $--border-radius-base; $--input-border-color-hover: $--border-color-hover; /// color||Color|0 $--input-background-color: $--color-white; $--input-fill-disabled: $--disabled-fill-base; $--input-color-disabled: $--font-color-disabled-base; /// color||Color|0 $--input-icon-color: $--color-text-placeholder; /// color||Color|0 $--input-placeholder-color: $--color-text-placeholder; $--input-max-width: 314px; $--input-hover-border: $--border-color-hover; $--input-clear-hover-color: $--color-text-secondary; $--input-focus-border: $--color-primary; $--input-focus-fill: $--color-white; $--input-disabled-fill: $--disabled-fill-base; $--input-disabled-border: $--disabled-border-base; $--input-disabled-color: $--disabled-color-base; $--input-disabled-placeholder-color: $--color-text-placeholder; /// fontSize||Font|1 $--input-medium-font-size: 14px; /// height||Other|4 $--input-medium-height: 36px; /// fontSize||Font|1 $--input-small-font-size: 13px; /// height||Other|4 $--input-small-height: 32px; /// fontSize||Font|1 $--input-mini-font-size: 12px; /// height||Other|4 $--input-mini-height: 28px; /* Cascader -------------------------- */ /// color||Color|0 $--cascader-menu-font-color: $--color-text-regular; /// color||Color|0 $--cascader-menu-selected-font-color: $--color-primary; $--cascader-menu-fill: $--fill-base; $--cascader-menu-font-size: $--font-size-base; $--cascader-menu-radius: $--border-radius-base; $--cascader-menu-border: solid 1px $--border-color-light; $--cascader-menu-shadow: $--box-shadow-light; $--cascader-node-background-hover: $--background-color-base; $--cascader-node-color-disabled: $--color-text-placeholder; $--cascader-color-empty: $--color-text-placeholder; $--cascader-tag-background: #f0f2f5; /* Group -------------------------- */ $--group-option-flex: 0 0 (1/5) * 100%; $--group-option-offset-bottom: 12px; $--group-option-fill-hover: rgba($--color-black, 0.06); $--group-title-color: $--color-black; $--group-title-font-size: $--font-size-base; $--group-title-width: 66px; /* Tab -------------------------- */ $--tab-font-size: $--font-size-base; $--tab-border-line: 1px solid #e4e4e4; $--tab-header-color-active: $--color-text-secondary; $--tab-header-color-hover: $--color-text-regular; $--tab-header-color: $--color-text-regular; $--tab-header-fill-active: rgba($--color-black, 0.06); $--tab-header-fill-hover: rgba($--color-black, 0.06); $--tab-vertical-header-width: 90px; $--tab-vertical-header-count-color: $--color-white; $--tab-vertical-header-count-fill: $--color-text-secondary; /* Button -------------------------- */ /// fontSize||Font|1 $--button-font-size: $--font-size-base; /// fontWeight||Font|1 $--button-font-weight: $--font-weight-primary; /// borderRadius||Border|2 $--button-border-radius: $--border-radius-base; /// padding||Spacing|3 $--button-padding-vertical: 12px; /// padding||Spacing|3 $--button-padding-horizontal: 20px; /// fontSize||Font|1 $--button-medium-font-size: $--font-size-base; /// borderRadius||Border|2 $--button-medium-border-radius: $--border-radius-base; /// padding||Spacing|3 $--button-medium-padding-vertical: 10px; /// padding||Spacing|3 $--button-medium-padding-horizontal: 20px; /// fontSize||Font|1 $--button-small-font-size: 12px; $--button-small-border-radius: $--border-radius-base; /// padding||Spacing|3 $--button-small-padding-vertical: 9px; /// padding||Spacing|3 $--button-small-padding-horizontal: 15px; /// fontSize||Font|1 $--button-mini-font-size: 12px; $--button-mini-border-radius: $--border-radius-base; /// padding||Spacing|3 $--button-mini-padding-vertical: 7px; /// padding||Spacing|3 $--button-mini-padding-horizontal: 15px; /// color||Color|0 $--button-default-font-color: $--color-text-regular; /// color||Color|0 $--button-default-background-color: $--color-white; /// color||Color|0 $--button-default-border-color: $--border-color-base; /// color||Color|0 $--button-disabled-font-color: $--color-text-placeholder; /// color||Color|0 $--button-disabled-background-color: $--color-white; /// color||Color|0 $--button-disabled-border-color: $--border-color-lighter; /// color||Color|0 $--button-primary-border-color: $--color-primary; /// color||Color|0 $--button-primary-font-color: $--color-white; /// color||Color|0 $--button-primary-background-color: $--color-primary; /// color||Color|0 $--button-success-border-color: $--color-success; /// color||Color|0 $--button-success-font-color: $--color-white; /// color||Color|0 $--button-success-background-color: $--color-success; /// color||Color|0 $--button-warning-border-color: $--color-warning; /// color||Color|0 $--button-warning-font-color: $--color-white; /// color||Color|0 $--button-warning-background-color: $--color-warning; /// color||Color|0 $--button-danger-border-color: $--color-danger; /// color||Color|0 $--button-danger-font-color: $--color-white; /// color||Color|0 $--button-danger-background-color: $--color-danger; /// color||Color|0 $--button-info-border-color: $--color-info; /// color||Color|0 $--button-info-font-color: $--color-white; /// color||Color|0 $--button-info-background-color: $--color-info; $--button-hover-tint-percent: 20%; $--button-active-shade-percent: 10%; /* cascader -------------------------- */ $--cascader-height: 200px; /* Switch -------------------------- */ /// color||Color|0 $--switch-on-color: $--color-primary; /// color||Color|0 $--switch-off-color: $--border-color-base; /// fontSize||Font|1 $--switch-font-size: $--font-size-base; $--switch-core-border-radius: 10px; // height||Other|4 TODO: width 代码写死的40px 所以下面这三个属性都没意义 $--switch-width: 40px; // height||Other|4 $--switch-height: 20px; // height||Other|4 $--switch-button-size: 16px; /* Dialog -------------------------- */ $--dialog-background-color: $--color-white; $--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); /// fontSize||Font|1 $--dialog-title-font-size: $--font-size-large; /// fontSize||Font|1 $--dialog-content-font-size: 14px; /// fontLineHeight||LineHeight|2 $--dialog-font-line-height: $--font-line-height-primary; /// padding||Spacing|3 $--dialog-padding-primary: 20px; /* Table -------------------------- */ /// color||Color|0 $--table-border-color: $--border-color-lighter; $--table-border: 1px solid $--table-border-color; /// color||Color|0 $--table-font-color: $--color-text-regular; /// color||Color|0 $--table-header-font-color: $--color-text-secondary; /// color||Color|0 $--table-row-hover-background-color: $--background-color-base; $--table-current-row-background-color: $--color-primary-light-9; /// color||Color|0 $--table-header-background-color: $--color-white; $--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, 0.12); /* Pagination -------------------------- */ /// fontSize||Font|1 $--pagination-font-size: 13px; /// color||Color|0 $--pagination-background-color: $--color-white; /// color||Color|0 $--pagination-font-color: $--color-text-primary; $--pagination-border-radius: $--border-radius-base; /// color||Color|0 $--pagination-button-color: $--color-text-primary; /// height||Other|4 $--pagination-button-width: 35.5px; /// height||Other|4 $--pagination-button-height: 28px; /// color||Color|0 $--pagination-button-disabled-color: $--color-text-placeholder; /// color||Color|0 $--pagination-button-disabled-background-color: $--color-white; /// color||Color|0 $--pagination-hover-color: $--color-primary; /* Popup -------------------------- */ /// color||Color|0 $--popup-modal-background-color: $--color-black; /// opacity||Other|1 $--popup-modal-opacity: 0.5; /* Popover -------------------------- */ /// color||Color|0 $--popover-background-color: $--color-white; /// fontSize||Font|1 $--popover-font-size: $--font-size-base; /// color||Color|0 $--popover-border-color: $--border-color-lighter; $--popover-arrow-size: 6px; /// padding||Spacing|3 $--popover-padding: 12px; $--popover-padding-large: 18px 20px; /// fontSize||Font|1 $--popover-title-font-size: 16px; /// color||Color|0 $--popover-title-font-color: $--color-text-primary; /* Tooltip -------------------------- */ /// color|1|Color|0 $--tooltip-fill: $--color-text-primary; /// color|1|Color|0 $--tooltip-color: $--color-white; /// fontSize||Font|1 $--tooltip-font-size: 12px; /// color||Color|0 $--tooltip-border-color: $--color-text-primary; $--tooltip-arrow-size: 6px; /// padding||Spacing|3 $--tooltip-padding: 10px; /* Tag -------------------------- */ /// color||Color|0 $--tag-info-color: $--color-info; /// color||Color|0 $--tag-primary-color: $--color-primary; /// color||Color|0 $--tag-success-color: $--color-success; /// color||Color|0 $--tag-warning-color: $--color-warning; /// color||Color|0 $--tag-danger-color: $--color-danger; /// fontSize||Font|1 $--tag-font-size: 12px; $--tag-border-radius: $--border-radius-base; $--tag-padding: 0 10px; /* Tree -------------------------- */ /// color||Color|0 $--tree-node-hover-background-color: $--background-color-base; /// color||Color|0 $--tree-font-color: $--color-text-regular; /// color||Color|0 $--tree-expand-icon-color: $--color-text-placeholder; /* Dropdown -------------------------- */ $--dropdown-menu-box-shadow: $--box-shadow-light; $--dropdown-menuItem-hover-fill: $--color-primary-light-9; $--dropdown-menuItem-hover-color: $--link-color; /* Badge -------------------------- */ /// color||Color|0 $--badge-background-color: $--color-danger; $--badge-radius: 10px; /// fontSize||Font|1 $--badge-font-size: 12px; /// padding||Spacing|3 $--badge-padding: 6px; /// height||Other|4 $--badge-size: 18px; /* Card -------------------------- */ /// color||Color|0 $--card-border-color: $--border-color-lighter; $--card-border-radius: $--border-radius-base; /// padding||Spacing|3 $--card-padding: 20px; /* Slider -------------------------- */ /// color||Color|0 $--slider-main-background-color: $--color-primary; /// color||Color|0 $--slider-runway-background-color: $--border-color-light; $--slider-button-hover-color: mix($--color-primary, black, 97%); $--slider-stop-background-color: $--color-white; $--slider-disable-color: $--color-text-placeholder; $--slider-margin: 16px 0; $--slider-border-radius: $--border-radius-base; /// height|1|Other|4 $--slider-height: 6px; /// height||Other|4 $--slider-button-size: 16px; $--slider-button-wrapper-size: 36px; $--slider-button-wrapper-offset: -15px; /* Steps -------------------------- */ $--steps-border-color: $--disabled-border-base; $--steps-border-radius: $--border-radius-base; $--steps-padding: 20px; /* Menu -------------------------- */ /// fontSize||Font|1 $--menu-item-font-size: $--font-size-base; /// color||Color|0 $--menu-item-font-color: $--color-text-primary; /// color||Color|0 $--menu-background-color: $--color-white; $--menu-item-hover-fill: $--color-primary-light-9; /* Rate -------------------------- */ $--rate-height: 20px; /// fontSize||Font|1 $--rate-font-size: $--font-size-base; /// height||Other|3 $--rate-icon-size: 18px; /// margin||Spacing|2 $--rate-icon-margin: 6px; $--rate-icon-color: $--color-text-placeholder; /* DatePicker -------------------------- */ $--datepicker-font-color: $--color-text-regular; /// color|1|Color|0 $--datepicker-off-font-color: $--color-text-placeholder; /// color||Color|0 $--datepicker-header-font-color: $--color-text-regular; $--datepicker-icon-color: $--color-text-primary; $--datepicker-border-color: $--disabled-border-base; $--datepicker-inner-border-color: #e4e4e4; /// color||Color|0 $--datepicker-inrange-background-color: $--border-color-extra-light; /// color||Color|0 $--datepicker-inrange-hover-background-color: $--border-color-extra-light; /// color||Color|0 $--datepicker-active-color: $--color-primary; /// color||Color|0 $--datepicker-hover-font-color: $--color-primary; $--datepicker-cell-hover-color: #fff; /* Loading -------------------------- */ /// height||Other|4 $--loading-spinner-size: 42px; /// height||Other|4 $--loading-fullscreen-spinner-size: 50px; /* Scrollbar -------------------------- */ $--scrollbar-background-color: rgba($--color-text-secondary, 0.3); $--scrollbar-hover-background-color: rgba($--color-text-secondary, 0.5); /* Carousel -------------------------- */ /// fontSize||Font|1 $--carousel-arrow-font-size: 12px; $--carousel-arrow-size: 36px; $--carousel-arrow-background: rgba(31, 45, 61, 0.11); $--carousel-arrow-hover-background: rgba(31, 45, 61, 0.23); /// width||Other|4 $--carousel-indicator-width: 30px; /// height||Other|4 $--carousel-indicator-height: 2px; $--carousel-indicator-padding-horizontal: 4px; $--carousel-indicator-padding-vertical: 12px; $--carousel-indicator-out-color: $--border-color-hover; /* Collapse -------------------------- */ /// color||Color|0 $--collapse-border-color: $--border-color-lighter; /// height||Other|4 $--collapse-header-height: 48px; /// color||Color|0 $--collapse-header-background-color: $--color-white; /// color||Color|0 $--collapse-header-font-color: $--color-text-primary; /// fontSize||Font|1 $--collapse-header-font-size: 13px; /// color||Color|0 $--collapse-content-background-color: $--color-white; /// fontSize||Font|1 $--collapse-content-font-size: 13px; /// color||Color|0 $--collapse-content-font-color: $--color-text-primary; /* Transfer -------------------------- */ $--transfer-border-color: $--border-color-lighter; $--transfer-border-radius: $--border-radius-base; /// height||Other|4 $--transfer-panel-width: 200px; /// height||Other|4 $--transfer-panel-header-height: 40px; /// color||Color|0 $--transfer-panel-header-background-color: $--background-color-base; /// height||Other|4 $--transfer-panel-footer-height: 40px; /// height||Other|4 $--transfer-panel-body-height: 246px; /// height||Other|4 $--transfer-item-height: 30px; /// height||Other|4 $--transfer-filter-height: 32px; /* Header -------------------------- */ $--header-padding: 0 20px; /* Footer -------------------------- */ $--footer-padding: 0 20px; /* Main -------------------------- */ $--main-padding: 20px; /* Timeline -------------------------- */ $--timeline-node-size-normal: 12px; $--timeline-node-size-large: 14px; $--timeline-node-color: $--border-color-light; /* Backtop -------------------------- */ /// color||Color|0 $--backtop-background-color: $--color-white; /// color||Color|0 $--backtop-font-color: $--color-primary; /// color||Color|0 $--backtop-hover-background-color: $--border-color-extra-light; /* Link -------------------------- */ /// fontSize||Font|1 $--link-font-size: $--font-size-base; /// fontWeight||Font|1 $--link-font-weight: $--font-weight-primary; /// color||Color|0 $--link-default-font-color: $--color-text-regular; /// color||Color|0 $--link-default-active-color: $--color-primary; /// color||Color|0 $--link-disabled-font-color: $--color-text-placeholder; /// color||Color|0 $--link-primary-font-color: $--color-primary; /// color||Color|0 $--link-success-font-color: $--color-success; /// color||Color|0 $--link-warning-font-color: $--color-warning; /// color||Color|0 $--link-danger-font-color: $--color-danger; /// color||Color|0 $--link-info-font-color: $--color-info; /* Calendar -------------------------- */ /// border||Other|4 $--calendar-border: $--table-border; /// color||Other|4 $--calendar-selected-background-color: #f2f8fe; $--calendar-cell-width: 85px; /* Form -------------------------- */ /// fontSize||Font|1 $--form-label-font-size: $--font-size-base; /* Avatar -------------------------- */ /// color||Color|0 $--avatar-font-color: #fff; /// color||Color|0 $--avatar-background-color: #c0c4cc; /// fontSize||Font Size|1 $--avatar-text-font-size: 14px; /// fontSize||Font Size|1 $--avatar-icon-font-size: 18px; /// borderRadius||Border|2 $--avatar-border-radius: $--border-radius-base; /// size|1|Avatar Size|3 $--avatar-large-size: 40px; /// size|1|Avatar Size|3 $--avatar-medium-size: 36px; /// size|1|Avatar Size|3 $--avatar-small-size: 28px; /* Break-point -------------------------- */ $--sm: 768px; $--md: 992px; $--lg: 1200px; $--xl: 1920px; $--breakpoints: ( "xs": ( max-width: $--sm - 1, ), "sm": ( min-width: $--sm, ), "md": ( min-width: $--md, ), "lg": ( min-width: $--lg, ), "xl": ( min-width: $--xl, ), ); $--breakpoints-spec: ( "xs-only": ( max-width: $--sm - 1, ), "sm-and-up": ( min-width: $--sm, ), "sm-only": "(min-width: #{$--sm}) and (max-width: #{$--md - 1})", "sm-and-down": ( max-width: $--md - 1, ), "md-and-up": ( min-width: $--md, ), "md-only": "(min-width: #{$--md}) and (max-width: #{$--lg - 1})", "md-and-down": ( max-width: $--lg - 1, ), "lg-and-up": ( min-width: $--lg, ), "lg-only": "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})", "lg-and-down": ( max-width: $--xl - 1, ), "xl-only": ( min-width: $--xl, ), ); @import "~element-ui/packages/theme-chalk/src/index"; ================================================ FILE: src/styles/loading.scss ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 全局加载动画 */ @charset "utf-8"; @import "./spinner/dots.css"; @import "./spinner/gauge.css"; @import "./spinner/inner-circles.css"; @import "./spinner/plus.css"; $base-loading: ".vab-loading-type"; /* 自定义loading开始 */ #{$base-loading}1 { display: flex; width: 36px; height: 36px; margin: 0 auto 15px; border: 3px solid transparent; border-top-color: $base-color-blue; border-bottom-color: $base-color-blue; border-radius: 50%; animation: vabLoading1-0 0.8s linear infinite; } #{$base-loading}1::before { display: block; width: 8px; height: 8px; margin: auto; content: ""; border: 3px solid $base-color-blue; border-radius: 50%; animation: vabLoading1 0.5s alternate ease-in infinite; } @keyframes vabLoading1-0 { to { transform: rotate(360deg); } } @keyframes vabLoading1 { from { transform: scale(0.5); } to { transform: scale(1.2); } } #{$base-loading}2 { width: 20px; height: 20px; margin-top: -40px; margin-left: -10px; animation: vabLoading2 1s linear reverse infinite; } #{$base-loading}2::before { display: block; width: 36px; height: 36px; margin-top: -17px; margin-left: -18px; content: ""; animation: vabLoading2 0.4s linear infinite; } #{$base-loading}2::after { display: block; width: 8px; height: 8px; margin-top: -3px; margin-left: -4px; content: ""; animation: vabLoading2 0.4s linear infinite; } #{$base-loading}2::before, #{$base-loading}2, #{$base-loading}2::after { position: absolute; top: 40%; left: 50%; border: 3px solid transparent; border-top-color: $base-color-blue; border-right-color: $base-color-blue; border-radius: 50%; } @keyframes vabLoading2 { to { transform: rotate(360deg); } } #{$base-loading}3 { display: inline-block; width: 2.5em; height: 3em; margin-bottom: 15px; border: 3px solid transparent; border-top-color: $base-color-blue; border-bottom-color: $base-color-blue; border-radius: 50%; animation: vabLoading3 2s ease infinite; } @keyframes vabLoading3 { 50% { border-width: 8px; transform: rotate(360deg) scale(0.4, 0.33); } 100% { border-width: 3px; transform: rotate(720deg) scale(1, 1); } } #{$base-loading}4 { display: inline-block; width: 30px; height: 30px; margin: 0 auto 10px; border: 8px solid transparent; border-bottom-color: $base-color-blue; border-left-color: $base-color-blue; border-radius: 50%; animation: vabLoading4 1s linear infinite normal; } #{$base-loading}4::after { display: block; width: 15px; height: 15px; margin: 0; content: " "; border: 6px solid $base-color-blue; border-bottom-color: transparent; border-left-color: transparent; border-radius: 50%; } @keyframes vabLoading4 { 0% { opacity: 0.2; transform: rotate(0deg); } 50% { opacity: 1; transform: rotate(180deg); } 100% { opacity: 0.2; transform: rotate(360deg); } } #{$base-loading}5 { display: block; width: 0; height: 0; margin: 0 auto 15px; border: solid 1.5em $base-color-blue; border-right: solid 1.5em transparent; border-left: solid 1.5em transparent; border-radius: 100%; animation: vabLoading5 1s linear infinite; } @keyframes vabLoading5 { 0% { transform: rotate(0deg); } 50% { transform: rotate(60deg); } 100% { transform: rotate(360deg); } } #{$base-loading}6 { display: block; width: 0; height: 0; margin: 0 auto 25px auto; perspective: 200px; } #{$base-loading}6::before, #{$base-loading}6::after { position: absolute; width: 20px; height: 20px; content: ""; background: rgba(0, 0, 0, 0); animation: vabLoading6 0.5s infinite alternate; } #{$base-loading}6::before { left: 0; } #{$base-loading}6::after { right: 0; animation-delay: 0.15s; } @keyframes vabLoading6 { 0% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); transform: scale(1) translateY(0) rotateX(0deg); } 100% { background: $base-color-blue; box-shadow: 0 25px 40px rgba($base-color-blue, 0.5); transform: scale(1.2) translateY(-25px) rotateX(45deg); } } #{$base-loading}7 { display: block; width: 25px; height: 25px; margin: 0 auto 15px auto; border: 2px solid $base-color-blue; border-top-color: rgba($base-color-blue, 0.2); border-right-color: rgba($base-color-blue, 0.2); border-bottom-color: rgba($base-color-blue, 0.2); border-radius: 100%; animation: vabLoading7 infinite 0.75s linear; } @keyframes vabLoading7 { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } } #{$base-loading}8 { position: relative; box-sizing: border-box; display: block; width: 20px; height: 20px; margin: 0 auto 15px auto; background-color: $base-color-blue; border-radius: 50%; box-shadow: 30px 0 0 0 $base-color-blue; transform: translateX(-15px); } #{$base-loading}8::after { position: absolute; top: 8px; left: 9px; width: 10px; height: 10px; content: ""; background-color: $base-color-white; border-radius: 50%; box-shadow: 30px 0 0 0 $base-color-white; animation: vabLoading8 2s ease-in-out infinite alternate; } @keyframes vabLoading8 { 0% { left: 9px; } 100% { left: 1px; } } #{$base-loading}9 { position: relative; box-sizing: border-box; display: block; width: 20px; height: 20px; margin: 0 auto 15px auto; border: 1px $base-color-blue solid; animation: vabLoading9 5s linear infinite; } #{$base-loading}9::after { position: absolute; top: -8px; left: 0; width: 4px; height: 4px; content: ""; background-color: $base-color-blue; animation: vabLoading9_check 1s ease-in-out infinite; } @keyframes vabLoading9_check { 25% { top: -8px; left: 22px; } 50% { top: 22px; left: 22px; } 75% { top: 22px; left: -9px; } 100% { top: -7px; left: -9px; } } @keyframes vabLoading9 { 0% { box-shadow: inset 0 0 0 0 rgba($base-color-blue, 0.5); opacity: 0.5; } 100% { box-shadow: inset 0 -20px 0 0 $base-color-blue; } } /* 自定义loading结束 */ ================================================ FILE: src/styles/normalize.scss ================================================ @charset "utf-8"; /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ /* Document ========================================================================== */ /** * 1. Correct the line height in all browsers. * 2. Prevent adjustments of font size after orientation changes in iOS. */ html { line-height: 1.15; /* 1 */ -webkit-text-size-adjust: 100%; /* 2 */ } /* Sections ========================================================================== */ /** * Remove the margin in all browsers. */ body { margin: 0; } /** * Render the `main` element consistently in IE. */ main { display: block; } /** * Correct the font size and margin on `h1` elements within `section` and * `article` contexts in Chrome, Firefox, and Safari. */ h1 { margin: 0.67em 0; font-size: 2em; } /* Grouping content ========================================================================== */ /** * 1. Add the correct box sizing in Firefox. * 2. Show the overflow in Edge and IE. */ hr { box-sizing: content-box; /* 1 */ height: 0; /* 1 */ overflow: visible; /* 2 */ } /** * 1. Correct the inheritance and scaling of font size in all browsers. * 2. Correct the odd `em` font sizing in all browsers. */ pre { font-family: monospace; /* 1 */ font-size: 1em; /* 2 */ } /* Text-level semantics ========================================================================== */ /** * Remove the gray background on active links in IE 10. */ a { background-color: transparent; } /** * 1. Remove the bottom border in Chrome 57- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. */ abbr[title] { text-decoration: underline; /* 2 */ text-decoration: underline dotted; /* 2 */ border-bottom: none; /* 1 */ } /** * Add the correct font weight in Chrome, Edge, and Safari. */ b, strong { font-weight: bolder; } /** * 1. Correct the inheritance and scaling of font size in all browsers. * 2. Correct the odd `em` font sizing in all browsers. */ code, kbd, samp { font-family: monospace; /* 1 */ font-size: 1em; /* 2 */ } /** * Add the correct font size in all browsers. */ small { font-size: 80%; } /** * Prevent `sub` and `sup` elements from affecting the line height in * all browsers. */ sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sub { bottom: -0.25em; } sup { top: -0.5em; } /* Embedded content ========================================================================== */ /** * Remove the border on images inside links in IE 10. */ img { border-style: none; } /* Forms ========================================================================== */ /** * 1. Change the font styles in all browsers. * 2. Remove the margin in Firefox and Safari. */ button, input, optgroup, select, textarea { margin: 0; /* 2 */ font-family: inherit; /* 1 */ font-size: 100%; /* 1 */ line-height: 1.15; /* 1 */ } /** * Show the overflow in IE. * 1. Show the overflow in Edge. */ button, input { /* 1 */ overflow: visible; } /** * Remove the inheritance of text transform in Edge, Firefox, and IE. * 1. Remove the inheritance of text transform in Firefox. */ button, select { /* 1 */ text-transform: none; } /** * Correct the inability to style clickable types in iOS and Safari. */ button, [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; } /** * Remove the inner border and padding in Firefox. */ button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { padding: 0; border-style: none; } /** * Restore the focus styles unset by the previous rule. */ button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { outline: 1px dotted ButtonText; } /** * Correct the padding in Firefox. */ fieldset { padding: 0.35em 0.75em 0.625em; } /** * 1. Correct the text wrapping in Edge and IE. * 2. Correct the color inheritance from `fieldset` elements in IE. * 3. Remove the padding so developers are not caught out when they zero out * `fieldset` elements in all browsers. */ legend { box-sizing: border-box; /* 1 */ display: table; /* 1 */ max-width: 100%; /* 1 */ padding: 0; /* 3 */ color: inherit; /* 2 */ white-space: normal; /* 1 */ } /** * Add the correct vertical alignment in Chrome, Firefox, and Opera. */ progress { vertical-align: baseline; } /** * Remove the default vertical scrollbar in IE 10+. */ textarea { overflow: auto; } /** * 1. Add the correct box sizing in IE 10. * 2. Remove the padding in IE 10. */ [type="checkbox"], [type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** * Correct the cursor style of increment and decrement buttons in Chrome. */ [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; } /** * 1. Correct the odd appearance in Chrome and Safari. * 2. Correct the outline style in Safari. */ [type="search"] { -webkit-appearance: textfield; /* 1 */ outline-offset: -2px; /* 2 */ } /** * Remove the inner padding in Chrome and Safari on macOS. */ [type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** * 1. Correct the inability to style clickable types in iOS and Safari. * 2. Change font properties to `inherit` in Safari. */ ::-webkit-file-upload-button { -webkit-appearance: button; /* 1 */ font: inherit; /* 2 */ } /* Interactive ========================================================================== */ /* * Add the correct display in Edge, IE 10+, and Firefox. */ details { display: block; } /* * Add the correct display in all browsers. */ summary { display: list-item; } /* Misc ========================================================================== */ /** * Add the correct display in IE 10+. */ template { display: none; } /** * Add the correct display in IE 10. */ [hidden] { display: none; } ================================================ FILE: src/styles/spinner/dots.css ================================================ .dots-loader:not(:required) { position: relative; display: inline-block; width: 7px; height: 7px; margin-bottom: 30px; overflow: hidden; text-indent: -9999px; background: transparent; border-radius: 100%; box-shadow: #f86 -14px -14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px 14px 0 7px, #4ae -14px 14px 0 7px; transform-origin: 50% 50%; animation: dots-loader 5s infinite ease-in-out; } @keyframes dots-loader { 0% { box-shadow: #f86 -14px -14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px 14px 0 7px, #4ae -14px 14px 0 7px; } 8.33% { box-shadow: #f86 14px -14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px 14px 0 7px, #4ae -14px 14px 0 7px; } 16.67% { box-shadow: #f86 14px 14px 0 7px, #fc6 14px 14px 0 7px, #6d7 14px 14px 0 7px, #4ae -14px 14px 0 7px; } 25% { box-shadow: #f86 -14px 14px 0 7px, #fc6 -14px 14px 0 7px, #6d7 -14px 14px 0 7px, #4ae -14px 14px 0 7px; } 33.33% { box-shadow: #f86 -14px -14px 0 7px, #fc6 -14px 14px 0 7px, #6d7 -14px -14px 0 7px, #4ae -14px -14px 0 7px; } 41.67% { box-shadow: #f86 14px -14px 0 7px, #fc6 -14px 14px 0 7px, #6d7 -14px -14px 0 7px, #4ae 14px -14px 0 7px; } 50% { box-shadow: #f86 14px 14px 0 7px, #fc6 -14px 14px 0 7px, #6d7 -14px -14px 0 7px, #4ae 14px -14px 0 7px; } 58.33% { box-shadow: #f86 -14px 14px 0 7px, #fc6 -14px 14px 0 7px, #6d7 -14px -14px 0 7px, #4ae 14px -14px 0 7px; } 66.67% { box-shadow: #f86 -14px -14px 0 7px, #fc6 -14px -14px 0 7px, #6d7 -14px -14px 0 7px, #4ae 14px -14px 0 7px; } 75% { box-shadow: #f86 14px -14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px -14px 0 7px, #4ae 14px -14px 0 7px; } 83.33% { box-shadow: #f86 14px 14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px 14px 0 7px, #4ae 14px 14px 0 7px; } 91.67% { box-shadow: #f86 -14px 14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px 14px 0 7px, #4ae -14px 14px 0 7px; } 100% { box-shadow: #f86 -14px -14px 0 7px, #fc6 14px -14px 0 7px, #6d7 14px 14px 0 7px, #4ae -14px 14px 0 7px; } } ================================================ FILE: src/styles/spinner/gauge.css ================================================ .gauge-loader:not(:required) { position: relative; display: inline-block; width: 64px; height: 32px; margin-bottom: 10px; overflow: hidden; text-indent: -9999px; background: #6ca; border-top-left-radius: 32px; border-top-right-radius: 32px; } .gauge-loader:not(:required)::before { position: absolute; top: 5px; left: 30px; width: 4px; height: 27px; content: ""; background: white; border-radius: 2px; transform-origin: 50% 100%; animation: gauge-loader 4000ms infinite ease; } .gauge-loader:not(:required)::after { position: absolute; top: 26px; left: 26px; width: 13px; height: 13px; content: ""; background: white; -moz-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; } @keyframes gauge-loader { 0% { transform: rotate(-50deg); } 10% { transform: rotate(20deg); } 20% { transform: rotate(60deg); } 24% { transform: rotate(60deg); } 40% { transform: rotate(-20deg); } 54% { transform: rotate(70deg); } 56% { transform: rotate(78deg); } 58% { transform: rotate(73deg); } 60% { transform: rotate(75deg); } 62% { transform: rotate(70deg); } 70% { transform: rotate(-20deg); } 80% { transform: rotate(20deg); } 83% { transform: rotate(25deg); } 86% { transform: rotate(20deg); } 89% { transform: rotate(25deg); } 100% { transform: rotate(-50deg); } } ================================================ FILE: src/styles/spinner/inner-circles.css ================================================ .inner-circles-loader:not(:required) { position: relative; display: inline-block; width: 50px; height: 50px; margin-bottom: 10px; overflow: hidden; text-indent: -9999px; background: rgba(25, 165, 152, 0.5); border-radius: 50%; transform: translate3d(0, 0, 0); } .inner-circles-loader:not(:required)::before, .inner-circles-loader:not(:required)::after { position: absolute; top: 0; display: inline-block; width: 50px; height: 50px; content: ""; border-radius: 50%; } .inner-circles-loader:not(:required)::before { left: 0; background: #c7efcf; transform-origin: 0 50%; animation: inner-circles-loader 3s infinite; } .inner-circles-loader:not(:required)::after { right: 0; background: #eef5db; transform-origin: 100% 50%; animation: inner-circles-loader 3s 0.2s reverse infinite; } @keyframes inner-circles-loader { 0% { transform: rotate(0deg); } 50% { transform: rotate(360deg); } 100% { transform: rotate(0deg); } } ================================================ FILE: src/styles/spinner/plus.css ================================================ .plus-loader:not(:required) { position: relative; display: inline-block; width: 48px; height: 48px; margin-bottom: 10px; overflow: hidden; text-indent: -9999px; background: #f86; -moz-border-radius: 24px; -webkit-border-radius: 24px; border-radius: 24px; -moz-transform: rotateZ(90deg); -ms-transform: rotateZ(90deg); -webkit-transform: rotateZ(90deg); transform: rotateZ(90deg); -moz-transform-origin: 50% 50%; -ms-transform-origin: 50% 50%; -webkit-transform-origin: 50% 50%; transform-origin: 50% 50%; -moz-animation: plus-loader-background 3s infinite ease-in-out; -webkit-animation: plus-loader-background 3s infinite ease-in-out; animation: plus-loader-background 3s infinite ease-in-out; } .plus-loader:not(:required)::after { position: absolute; top: 0; right: 50%; width: 50%; height: 100%; content: ""; background: #f86; -moz-border-radius: 24px 0 0 24px; -webkit-border-radius: 24px; border-radius: 24px 0 0 24px; -moz-transform-origin: 100% 50%; -ms-transform-origin: 100% 50%; -webkit-transform-origin: 100% 50%; transform-origin: 100% 50%; -moz-animation: plus-loader-top 3s infinite linear; -webkit-animation: plus-loader-top 3s infinite linear; animation: plus-loader-top 3s infinite linear; } .plus-loader:not(:required)::before { position: absolute; top: 0; right: 50%; width: 50%; height: 100%; content: ""; background: #fc6; -moz-border-radius: 24px 0 0 24px; -webkit-border-radius: 24px; border-radius: 24px 0 0 24px; -moz-transform-origin: 100% 50%; -ms-transform-origin: 100% 50%; -webkit-transform-origin: 100% 50%; transform-origin: 100% 50%; -moz-animation: plus-loader-bottom 3s infinite linear; -webkit-animation: plus-loader-bottom 3s infinite linear; animation: plus-loader-bottom 3s infinite linear; } @keyframes plus-loader-top { 2.5% { background: #f86; -moz-transform: rotateY(0deg); -ms-transform: rotateY(0deg); -webkit-transform: rotateY(0deg); transform: rotateY(0deg); -moz-animation-timing-function: ease-in; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 13.75% { background: #ff430d; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 13.76% { background: #ffae0d; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: ease-out; -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 25% { background: #fc6; -moz-transform: rotateY(180deg); -ms-transform: rotateY(180deg); -webkit-transform: rotateY(180deg); transform: rotateY(180deg); } 27.5% { background: #fc6; -moz-transform: rotateY(180deg); -ms-transform: rotateY(180deg); -webkit-transform: rotateY(180deg); transform: rotateY(180deg); -moz-animation-timing-function: ease-in; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 41.25% { background: #ffae0d; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 41.26% { background: #2cc642; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: ease-out; -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 50% { background: #6d7; -moz-transform: rotateY(0deg); -ms-transform: rotateY(0deg); -webkit-transform: rotateY(0deg); transform: rotateY(0deg); } 52.5% { background: #6d7; -moz-transform: rotateY(0deg); -ms-transform: rotateY(0deg); -webkit-transform: rotateY(0deg); transform: rotateY(0deg); -moz-animation-timing-function: ease-in; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 63.75% { background: #2cc642; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 63.76% { background: #1386d2; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: ease-out; -webkit-animation-timing-function: ease-out; animation-timing-function: ease-out; } 75% { background: #4ae; -moz-transform: rotateY(180deg); -ms-transform: rotateY(180deg); -webkit-transform: rotateY(180deg); transform: rotateY(180deg); } 77.5% { background: #4ae; -moz-transform: rotateY(180deg); -ms-transform: rotateY(180deg); -webkit-transform: rotateY(180deg); transform: rotateY(180deg); -moz-animation-timing-function: ease-in; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 91.25% { background: #1386d2; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 91.26% { background: #ff430d; -moz-transform: rotateY(90deg); -ms-transform: rotateY(90deg); -webkit-transform: rotateY(90deg); transform: rotateY(90deg); -moz-animation-timing-function: ease-in; -webkit-animation-timing-function: ease-in; animation-timing-function: ease-in; } 100% { background: #f86; -moz-transform: rotateY(0deg); -ms-transform: rotateY(0deg); -webkit-transform: rotateY(0deg); transform: rotateY(0deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } } @keyframes plus-loader-bottom { 0% { background: #fc6; -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 50% { background: #fc6; -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 75% { background: #4ae; -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 100% { background: #4ae; -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } } @keyframes plus-loader-background { 0% { background: #f86; -moz-transform: rotateZ(180deg); -ms-transform: rotateZ(180deg); -webkit-transform: rotateZ(180deg); transform: rotateZ(180deg); } 25% { background: #f86; -moz-transform: rotateZ(180deg); -ms-transform: rotateZ(180deg); -webkit-transform: rotateZ(180deg); transform: rotateZ(180deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 27.5% { background: #6d7; -moz-transform: rotateZ(90deg); -ms-transform: rotateZ(90deg); -webkit-transform: rotateZ(90deg); transform: rotateZ(90deg); } 50% { background: #6d7; -moz-transform: rotateZ(90deg); -ms-transform: rotateZ(90deg); -webkit-transform: rotateZ(90deg); transform: rotateZ(90deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 52.5% { background: #6d7; -moz-transform: rotateZ(0deg); -ms-transform: rotateZ(0deg); -webkit-transform: rotateZ(0deg); transform: rotateZ(0deg); } 75% { background: #6d7; -moz-transform: rotateZ(0deg); -ms-transform: rotateZ(0deg); -webkit-transform: rotateZ(0deg); transform: rotateZ(0deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } 77.5% { background: #f86; -moz-transform: rotateZ(270deg); -ms-transform: rotateZ(270deg); -webkit-transform: rotateZ(270deg); transform: rotateZ(270deg); } 100% { background: #f86; -moz-transform: rotateZ(270deg); -ms-transform: rotateZ(270deg); -webkit-transform: rotateZ(270deg); transform: rotateZ(270deg); -moz-animation-timing-function: step-start; -webkit-animation-timing-function: step-start; animation-timing-function: step-start; } } ================================================ FILE: src/styles/themes/default.scss ================================================ /* 绿荫草场主题、荣耀典藏主题、暗黑之子主题加QQ讨论群972435319、1139183756后私聊群主获取,获取后将主题放到themes文件夹根目录即可 */ ================================================ FILE: src/styles/transition.scss ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description vue过渡动画 */ @charset "utf-8"; .fade-transform-leave-active, .fade-transform-enter-active { transition: $base-transition; } .fade-transform-enter { opacity: 0; } .fade-transform-leave-to { opacity: 0; } ================================================ FILE: src/styles/vab.scss ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 全局样式 */ @charset "utf-8"; @import './normalize.scss'; @import './transition.scss'; @import './loading.scss'; $base: '.vab'; @mixin scrollbar { max-height: 88vh; margin-bottom: 0.5vh; overflow-y: auto; &::-webkit-scrollbar { width: 0; height: 0; background: transparent; } &::-webkit-scrollbar-thumb { background-color: rgba(144, 147, 153, 0.3); border-radius: 10px; } &::-webkit-scrollbar-thumb:hover { background-color: rgba(144, 147, 153, 0.3); } } @mixin base-scrollbar { &::-webkit-scrollbar { width: 13px; height: 13px; } &::-webkit-scrollbar-thumb { background-color: rgba(0, 0, 0, 0.4); background-clip: padding-box; border: 3px solid transparent; border-radius: 7px; } &::-webkit-scrollbar-thumb:hover { background-color: rgba(0, 0, 0, 0.5); } &::-webkit-scrollbar-track { background-color: transparent; } &::-webkit-scrollbar-track:hover { background-color: #f8fafc; } } img { object-fit: cover; } a { color: $base-color-blue; text-decoration: none; cursor: pointer; } * { transition: $base-transition; } svg { transition: none; * { transition: none; } } html { body { position: relative; height: 100vh; padding: 0; margin: 0; font-family: Avenir, Helvetica, Arial, sans-serif; font-size: $base-font-size-default; color: #2c3e50; background: #f6f8f9; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @include base-scrollbar; div { @include base-scrollbar; } svg, i { &:hover { opacity: 0.8; } } .v-modal { backdrop-filter: blur(10px); } /* el-tag开始 */ .el-tag + .el-tag { margin-left: 10px; } /* el-tag结束 */ /* markdown编辑器开始 */ .editor-toolbar { .no-mobile, .fa-question-circle { display: none; } } /* markdown编辑器结束 */ /* 间隔线开始 */ .el-divider--horizontal { margin: 10px 0 25px 0; .el-divider__text { display: -webkit-box; overflow: hidden; text-overflow: ellipsis; -webkit-line-clamp: 1; -webkit-box-orient: vertical; } } /* 间隔线结束 */ /* 大图展示开始 */ .el-image-viewer { &__close { .el-icon-circle-close { color: $base-color-white; } } } /* 大图展示结束 */ .vue-admin-better-wrapper { .app-main-container { @include base-scrollbar; > [class*='-container'] { * { transition: none; } padding: $base-padding; background: $base-color-white; } } } /* 进度条开始 */ #nprogress { position: fixed; z-index: $base-z-index; .bar { background: $base-color-blue !important; } .peg { box-shadow: 0 0 10px $base-color-blue, 0 0 5px $base-color-blue !important; } } /* 进度条结束 */ /* 表格开始 */ .el-table { .el-table__body-wrapper { @include base-scrollbar; } th { background: #f5f7fa; } td, th { position: relative; box-sizing: border-box; padding: 7.5px 0; .cell { font-size: $base-font-size-default; font-weight: normal; color: #606266; .el-image { width: 50px; height: 50px; border-radius: $base-border-radius; } } } } /* 表格结束 */ /* 分页开始 */ .el-pagination { padding: 2px 5px; margin: 15px 0 0 0; font-weight: normal; color: $base-color-black; text-align: center; } /* 分页结束 */ /* 菜单开始 */ .el-menu.el-menu--popup.el-menu--popup-right-start { @include scrollbar; } .el-menu.el-menu--popup.el-menu--popup-bottom-start { @include scrollbar; } .el-submenu__title i { color: $base-color-white; } /* 菜单结束 */ /* 弹窗开始 */ .el-dialog, .el-message-box { &__body { border-top: 1px solid $base-border-color; .el-form { padding-right: 30px; } } &__footer { padding: $base-padding; text-align: right; border-top: 1px solid $base-border-color; } &__content { padding: 20px 20px 20px 20px; } } /* 弹窗结束 */ /* 卡片开始 */ .el-card { margin-bottom: 15px; &__body { padding: $base-padding; } } /* 卡片结束 */ /* 下拉树样式-----------开始 */ .select-tree-popper { .el-scrollbar { .el-scrollbar__view { .el-select-dropdown__item { height: auto; max-height: 274px; padding: 0; overflow-y: auto; line-height: 26px; } } } } /* 下拉树样式-----------结束 */ } } ================================================ FILE: src/styles/variables.scss ================================================ /** * @author https://vue-admin-better.com (不想保留author可删除) * @description 全局主题变量配置 */ /* stylelint-disable */ @charset "utf-8"; //框架默认主题色 $base-color-default: #409eff; //默认层级 $base-z-index: 999; //横向布局纵向布局时菜单背景色 $base-menu-background: #21252b; //菜单文字颜色 $base-menu-color: hsla(0, 0%, 100%, 0.95); //菜单选中文字颜色 $base-menu-color-active: hsla(0, 0%, 100%, 0.95); //菜单选中背景色 $base-menu-background-active: $base-color-default; //标题颜色 $base-title-color: #fff; //字体大小配置 $base-font-size-small: 12px; $base-font-size-default: 14px; $base-font-size-big: 16px; $base-font-size-bigger: 18px; $base-font-size-max: 22px; $base-font-color: #606266; $base-color-blue: $base-color-default; $base-color-green: #41b882; $base-color-white: #fff; $base-color-black: #000; $base-color-yellow: #ffa91b; $base-color-orange: #ff6700; $base-color-red: #f34d37; $base-color-gray: rgba(0, 0, 0, 0.65); $base-main-width: 1279px; $base-border-radius: 4px; $base-border-color: #dcdfe6; //输入框高度 $base-input-height: 32px; //默认paddiing $base-padding: 20px; //默认阴影 $base-box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); //横向布局时top-bar、logo、一级菜单的高度 $base-top-bar-height: 65px; //纵向布局时logo的高度 $base-logo-height: 75px; //顶部nav-bar的高度 $base-nav-bar-height: 60px; //顶部多标签页tabs-bar的高度 $base-tabs-bar-height: 55px; //顶部多标签页tabs-bar中每一个item的高度 $base-tag-item-height: 34px; //菜单li标签的高度 $base-menu-item-height: 50px; //app-main的高度 $base-app-main-height: calc( 100vh - #{$base-nav-bar-height} - #{$base-tabs-bar-height} - #{$base-padding} - #{$base-padding} - 55px - 55px ); //纵向布局时左侧导航未折叠时的宽度 $base-left-menu-width: 256px; //纵向布局时左侧导航未折叠时右侧内容的宽度 $base-right-content-width: calc(100% - #{$base-left-menu-width}); //纵向布局时左侧导航已折叠时的宽度 $base-left-menu-width-min: 65px; //纵向布局时左侧导航已折叠时右侧内容的宽度 $base-right-content-width-min: calc(100% - #{$base-left-menu-width-min}); //默认动画 $base-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border 0s, background 0s, color 0s, font-size 0s; //默认动画长 $base-transition-time: 0.3s; :export { //菜单文字颜色变量导出 menu-color: $base-menu-color; //菜单选中文字颜色变量导出 menu-color-active: $base-menu-color-active; //菜单背景色变量导出 menu-background: $base-menu-background; } ================================================ FILE: src/utils/accessToken.js ================================================ import { storage, tokenTableName } from '@/config' /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 获取accessToken * @returns {string|ActiveX.IXMLDOMNode|Promise|any|IDBRequest|MediaKeyStatus|FormDataEntryValue|Function|Promise} */ export function getAccessToken() { if (storage) { if ('localStorage' === storage) { return localStorage.getItem(tokenTableName) } else if ('sessionStorage' === storage) { return sessionStorage.getItem(tokenTableName) } else { return localStorage.getItem(tokenTableName) } } else { return localStorage.getItem(tokenTableName) } } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 存储accessToken * @param accessToken * @returns {void|*} */ export function setAccessToken(accessToken) { if (storage) { if ('localStorage' === storage) { return localStorage.setItem(tokenTableName, accessToken) } else if ('sessionStorage' === storage) { return sessionStorage.setItem(tokenTableName, accessToken) } else { return localStorage.setItem(tokenTableName, accessToken) } } else { return localStorage.setItem(tokenTableName, accessToken) } } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 移除accessToken * @returns {void|Promise} */ export function removeAccessToken() { if (storage) { if ('localStorage' === storage) { return localStorage.removeItem(tokenTableName) } else if ('sessionStorage' === storage) { return sessionStorage.clear() } else { return localStorage.removeItem(tokenTableName) } } else { return localStorage.removeItem(tokenTableName) } } ================================================ FILE: src/utils/encrypt.js ================================================ import { JSEncrypt } from 'jsencrypt' import { getPublicKey } from '@/api/publicKey' const privateKey = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMFPa+v52FkSUXvcUnrGI/XzW3EpZRI0s9BCWJ3oNQmEYA5luWW5p8h0uadTIoTyYweFPdH4hveyxlwmS7oefvbIdiP+o+QIYW/R4Wjsb4Yl8MhR4PJqUE3RCy6IT9fM8ckG4kN9ECs6Ja8fQFc6/mSl5dJczzJO3k1rWMBhKJD/AgMBAAECgYEAucMakH9dWeryhrYoRHcXo4giPVJsH9ypVt4KzmOQY/7jV7KFQK3x//27UoHfUCak51sxFw9ek7UmTPM4HjikA9LkYeE7S381b4QRvFuf3L6IbMP3ywJnJ8pPr2l5SqQ00W+oKv+w/VmEsyUHr+k4Z+4ik+FheTkVWp566WbqFsECQQDjYaMcaKw3j2Zecl8T6eUe7fdaRMIzp/gcpPMfT/9rDzIQk+7ORvm1NI9AUmFv/FAlfpuAMrdL2n7p9uznWb7RAkEA2aP934kbXg5bdV0R313MrL+7WTK/qdcYxATUbMsMuWWQBoS5irrt80WCZbG48hpocJavLNjbtrjmUX3CuJBmzwJAOJg8uP10n/+ZQzjEYXh+BszEHDuw+pp8LuT/fnOy5zrJA0dO0RjpXijO3vuiNPVgHXT9z1LQPJkNrb5ACPVVgQJBALPeb4uV0bNrJDUb5RB4ghZnIxv18CcaqNIft7vuGCcFBAIPIRTBprR+RuVq+xHDt3sNXdsvom4h49+Hky1b0ksCQBBwUtVaqH6ztCtwUF1j2c/Zcrt5P/uN7IHAd44K0gIJc1+Csr3qPG+G2yoqRM8KVqLI8Z2ZYn9c+AvEE+L9OQY=' /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description RSA加密 * @param data * @returns {Promise<{param: PromiseLike}|*>} */ export async function encryptedData(data) { let publicKey = '' const res = await getPublicKey() publicKey = res.data.publicKey if (res.data.mockServer) { publicKey = '' } if (publicKey == '') { return data } const encrypt = new JSEncrypt() encrypt.setPublicKey( `-----BEGIN PUBLIC KEY-----${publicKey}-----END PUBLIC KEY-----` ) data = encrypt.encrypt(JSON.stringify(data)) return { param: data, } } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description RSA解密 * @param data * @returns {PromiseLike} */ export function decryptedData(data) { const decrypt = new JSEncrypt() decrypt.setPrivateKey( `-----BEGIN RSA PRIVATE KEY-----${privateKey}-----END RSA PRIVATE KEY-----` ) data = decrypt.decrypt(JSON.stringify(data)) return data } ================================================ FILE: src/utils/errorLog.js ================================================ import Vue from 'vue' import store from '@/store' import { isArray, isString } from '@/utils/validate' import { errorLog } from '@/config' const needErrorLog = errorLog const checkNeed = () => { const env = process.env.NODE_ENV if (isString(needErrorLog)) { return env === needErrorLog } if (isArray(needErrorLog)) { return needErrorLog.includes(env) } return false } if (checkNeed()) { Vue.config.errorHandler = (err, vm, info) => { console.error('vue-admin-better错误拦截:', err, vm, info) const url = window.location.href Vue.nextTick(() => { store.dispatch('errorLog/addErrorLog', { err, vm, info, url }) }) } } ================================================ FILE: src/utils/handleRoutes.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description all模式渲染后端返回路由 * @param constantRoutes * @returns {*} */ export function convertRouter(asyncRoutes) { return asyncRoutes.map((route) => { if (route.component) { if (route.component === 'Layout') { route.component = (resolve) => require(['@/layouts'], resolve) } else if (route.component === 'EmptyLayout') { route.component = (resolve) => require(['@/layouts/EmptyLayout'], resolve) } else { const index = route.component.indexOf('views') const path = index > 0 ? route.component.slice(index) : `views/${route.component}` route.component = (resolve) => require([`@/${path}`], resolve) } } if (route.children && route.children.length) route.children = convertRouter(route.children) if (route.children && route.children.length === 0) delete route.children return route }) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断当前路由是否包含权限 * @param permissions * @param route * @returns {boolean|*} */ function hasPermission(permissions, route) { if (route.meta && route.meta.permissions) { return permissions.some((role) => route.meta.permissions.includes(role)) } else { return true } } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description intelligence模式根据permissions数组拦截路由 * @param routes * @param permissions * @returns {[]} */ export function filterAsyncRoutes(routes, permissions) { const finallyRoutes = [] routes.forEach((route) => { const item = { ...route } if (hasPermission(permissions, item)) { if (item.children) { item.children = filterAsyncRoutes(item.children, permissions) } finallyRoutes.push(item) } }) return finallyRoutes } ================================================ FILE: src/utils/index.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 格式化时间 * @param time * @param cFormat * @returns {string|null} */ export function parseTime(time, cFormat) { if (arguments.length === 0) { return null } const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' let date if (typeof time === 'object') { date = time } else { if (typeof time === 'string' && /^[0-9]+$/.test(time)) { time = parseInt(time) } if (typeof time === 'number' && time.toString().length === 10) { time = time * 1000 } date = new Date(time) } const formatObj = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay(), } const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { let value = formatObj[key] if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } if (result.length > 0 && value < 10) { value = '0' + value } return value || 0 }) return time_str } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 格式化时间 * @param time * @param option * @returns {string} */ export function formatTime(time, option) { if (('' + time).length === 10) { time = parseInt(time) * 1000 } else { time = +time } const d = new Date(time) const now = Date.now() const diff = (now - d) / 1000 if (diff < 30) { return '刚刚' } else if (diff < 3600) { // less 1 hour return Math.ceil(diff / 60) + '分钟前' } else if (diff < 3600 * 24) { return Math.ceil(diff / 3600) + '小时前' } else if (diff < 3600 * 24 * 2) { return '1天前' } if (option) { return parseTime(time, option) } else { return ( d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' ) } } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 将url请求参数转为json格式 * @param url * @returns {{}|any} */ export function paramObj(url) { const search = url.split('?')[1] if (!search) { return {} } return JSON.parse( '{"' + decodeURIComponent(search) .replace(/"/g, '\\"') .replace(/&/g, '","') .replace(/=/g, '":"') .replace(/\+/g, ' ') + '"}' ) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 父子关系的数组转换成树形结构数据 * @param data * @returns {*} */ export function translateDataToTree(data) { const parent = data.filter( (value) => value.parentId === 'undefined' || value.parentId == null ) const children = data.filter( (value) => value.parentId !== 'undefined' && value.parentId != null ) const translator = (parent, children) => { parent.forEach((parent) => { children.forEach((current, index) => { if (current.parentId === parent.id) { const temp = JSON.parse(JSON.stringify(children)) temp.splice(index, 1) translator([current], temp) typeof parent.children !== 'undefined' ? parent.children.push(current) : (parent.children = [current]) } }) }) } translator(parent, children) return parent } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 树形结构数据转换成父子关系的数组 * @param data * @returns {[]} */ export function translateTreeToData(data) { const result = [] data.forEach((item) => { const loop = (data) => { result.push({ id: data.id, name: data.name, parentId: data.parentId, }) const child = data.children if (child) { for (let i = 0; i < child.length; i++) { loop(child[i]) } } } loop(item) }) return result } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 10位时间戳转换 * @param time * @returns {string} */ export function tenBitTimestamp(time) { const date = new Date(time * 1000) const y = date.getFullYear() let m = date.getMonth() + 1 m = m < 10 ? '' + m : m let d = date.getDate() d = d < 10 ? '' + d : d let h = date.getHours() h = h < 10 ? '0' + h : h let minute = date.getMinutes() let second = date.getSeconds() minute = minute < 10 ? '0' + minute : minute second = second < 10 ? '0' + second : second return y + '年' + m + '月' + d + '日 ' + h + ':' + minute + ':' + second //组合 } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 13位时间戳转换 * @param time * @returns {string} */ export function thirteenBitTimestamp(time) { const date = new Date(time / 1) const y = date.getFullYear() let m = date.getMonth() + 1 m = m < 10 ? '' + m : m let d = date.getDate() d = d < 10 ? '' + d : d let h = date.getHours() h = h < 10 ? '0' + h : h let minute = date.getMinutes() let second = date.getSeconds() minute = minute < 10 ? '0' + minute : minute second = second < 10 ? '0' + second : second return y + '年' + m + '月' + d + '日 ' + h + ':' + minute + ':' + second //组合 } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 获取随机id * @param length * @returns {string} */ export function uuid(length = 32) { const num = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' let str = '' for (let i = 0; i < length; i++) { str += num.charAt(Math.floor(Math.random() * num.length)) } return str } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description m到n的随机数 * @param m * @param n * @returns {number} */ export function random(m, n) { return Math.floor(Math.random() * (m - n) + n) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description addEventListener * @type {function(...[*]=)} */ export const on = (function () { return function (element, event, handler, useCapture = false) { if (element && event && handler) { element.addEventListener(event, handler, useCapture) } } })() /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description removeEventListener * @type {function(...[*]=)} */ export const off = (function () { return function (element, event, handler, useCapture = false) { if (element && event) { element.removeEventListener(event, handler, useCapture) } } })() ================================================ FILE: src/utils/pageTitle.js ================================================ import { title } from '@/config' /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 设置标题 * @param pageTitle * @returns {string} */ export default function getPageTitle(pageTitle) { if (pageTitle) { return `${pageTitle}-${title}` } return `${title}` } ================================================ FILE: src/utils/permission.js ================================================ import store from '@/store' /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 检查权限 * @param value * @returns {boolean} */ export default function checkPermission(value) { if (value && value instanceof Array && value.length > 0) { const permissions = store.getters['user/permissions'] const permissionPermissions = value return permissions.some((role) => { return permissionPermissions.includes(role) }) } else { return false } } ================================================ FILE: src/utils/request.js ================================================ import Vue from 'vue' import axios from 'axios' import { baseURL, contentType, debounce, invalidCode, noPermissionCode, requestTimeout, successCode, tokenName, loginInterception, } from '@/config' import store from '@/store' import qs from 'qs' import router from '@/router' import { isArray } from '@/utils/validate' let loadingInstance /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 处理code异常 * @param {*} code * @param {*} msg */ const handleCode = (code, msg) => { switch (code) { case invalidCode: Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error') store.dispatch('user/resetAccessToken').catch(() => {}) if (loginInterception) { location.reload() } break case noPermissionCode: router.push({ path: '/401' }).catch(() => {}) break default: Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error') break } } const instance = axios.create({ baseURL, timeout: requestTimeout, headers: { 'Content-Type': contentType, }, }) instance.interceptors.request.use( (config) => { if (store.getters['user/accessToken']) { config.headers[tokenName] = store.getters['user/accessToken'] } //这里会过滤所有为空、0、false的key,如果不需要请自行注释 if (config.data) config.data = Vue.prototype.$baseLodash.pickBy( config.data, Vue.prototype.$baseLodash.identity ) if ( config.data && config.headers['Content-Type'] === 'application/x-www-form-urlencoded;charset=UTF-8' ) config.data = qs.stringify(config.data) if (debounce.some((item) => config.url.includes(item))) loadingInstance = Vue.prototype.$baseLoading() return config }, (error) => { return Promise.reject(error) } ) instance.interceptors.response.use( (response) => { if (loadingInstance) loadingInstance.close() const { data, config } = response const { code, msg } = data // 操作正常Code数组 const codeVerificationArray = isArray(successCode) ? [...successCode] : [...[successCode]] // 是否操作正常 if (codeVerificationArray.includes(code)) { return data } else { handleCode(code, msg) return Promise.reject( 'vue-admin-better请求异常拦截:' + JSON.stringify({ url: config.url, code, msg }) || 'Error' ) } }, (error) => { if (loadingInstance) loadingInstance.close() const { response, message } = error if (error.response && error.response.data) { const { status, data } = response handleCode(status, data.msg || message) return Promise.reject(error) } else { let { message } = error if (message === 'Network Error') { message = '后端接口连接异常' } if (message.includes('timeout')) { message = '后端接口请求超时' } if (message.includes('Request failed with status code')) { const code = message.substr(message.length - 3) message = '后端接口' + code + '异常' } Vue.prototype.$baseMessage(message || `后端接口未知异常`, 'error') return Promise.reject(error) } } ) export default instance ================================================ FILE: src/utils/static.js ================================================ /** * @author chuzhixin 1204505056@qq.com * @description 导入所有 controller 模块,浏览器环境中自动输出controller文件夹下Mock接口,请勿修改。 */ import Mock from 'mockjs' import { paramObj } from '@/utils/index' const mocks = [] const files = require.context('../../mock/controller', false, /\.js$/) files.keys().forEach((key) => { mocks.push(...files(key)) }) export function mockXHR() { Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send Mock.XHR.prototype.send = function () { if (this.custom.xhr) { this.custom.xhr.withCredentials = this.withCredentials || false if (this.responseType) { this.custom.xhr.responseType = this.responseType } } this.proxy_send(...arguments) } function XHRHttpRequst(respond) { return function (options) { let result if (respond instanceof Function) { const { body, type, url } = options result = respond({ method: type, body: JSON.parse(body), query: paramObj(url), }) } else { result = respond } return Mock.mock(result) } } mocks.forEach((item) => { Mock.mock( new RegExp(item.url), item.type || 'get', XHRHttpRequst(item.response) ) }) } ================================================ FILE: src/utils/vab.js ================================================ import { loadingText, messageDuration, title } from '@/config' import * as lodash from 'lodash' import { Loading, Message, MessageBox, Notification } from 'element-ui' import store from '@/store' import { getAccessToken } from '@/utils/accessToken' const accessToken = store.getters['user/accessToken'] const layout = store.getters['settings/layout'] const install = (Vue, opts = {}) => { /* 全局accessToken */ Vue.prototype.$baseAccessToken = () => { return accessToken || getAccessToken() } /* 全局标题 */ Vue.prototype.$baseTitle = (() => { return title })() /* 全局加载层 */ Vue.prototype.$baseLoading = (index, text) => { let loading if (!index) { loading = Loading.service({ lock: true, text: text || loadingText, background: 'hsla(0,0%,100%,.8)', }) } else { loading = Loading.service({ lock: true, text: text || loadingText, spinner: 'vab-loading-type' + index, background: 'hsla(0,0%,100%,.8)', }) } return loading } /* 全局多彩加载层 */ Vue.prototype.$baseColorfullLoading = (index, text) => { let loading if (!index) { loading = Loading.service({ lock: true, text: text || loadingText, spinner: 'dots-loader', background: 'hsla(0,0%,100%,.8)', }) } else { switch (index) { case 1: index = 'dots' break case 2: index = 'gauge' break case 3: index = 'inner-circles' break case 4: index = 'plus' break } loading = Loading.service({ lock: true, text: text || loadingText, spinner: index + '-loader', background: 'hsla(0,0%,100%,.8)', }) } return loading } /* 全局Message */ Vue.prototype.$baseMessage = (message, type) => { Message({ offset: 60, showClose: true, message: message, type: type, dangerouslyUseHTMLString: true, duration: messageDuration, }) } /* 全局Alert */ Vue.prototype.$baseAlert = (content, title, callback) => { MessageBox.alert(content, title || '温馨提示', { confirmButtonText: '确定', dangerouslyUseHTMLString: true, callback: (action) => { if (callback) { callback() } }, }) } /* 全局Confirm */ Vue.prototype.$baseConfirm = (content, title, callback1, callback2) => { MessageBox.confirm(content, title || '温馨提示', { confirmButtonText: '确定', cancelButtonText: '取消', closeOnClickModal: false, type: 'warning', }) .then(() => { if (callback1) { callback1() } }) .catch(() => { if (callback2) { callback2() } }) } /* 全局Notification */ Vue.prototype.$baseNotify = (message, title, type, position) => { Notification({ title: title, message: message, position: position || 'top-right', type: type || 'success', duration: messageDuration, }) } /* 全局TableHeight */ Vue.prototype.$baseTableHeight = (formType) => { let height = window.innerHeight let paddingHeight = 400 const formHeight = 50 if (layout === 'vertical') { paddingHeight = 340 } if ('number' == typeof formType) { height = height - paddingHeight - formHeight * formType } else { height = height - paddingHeight } return height } /* 全局lodash */ Vue.prototype.$baseLodash = lodash /* 全局事件总线 */ Vue.prototype.$baseEventBus = new Vue() } if (typeof window !== 'undefined' && window.Vue) { install(window.Vue) } export default install ================================================ FILE: src/utils/validate.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判读是否为外链 * @param path * @returns {boolean} */ export function isExternal(path) { return /^(https?:|mailto:|tel:)/.test(path) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 校验密码是否小于6位 * @param str * @returns {boolean} */ export function isPassword(str) { return str.length >= 6 } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否为数字 * @param value * @returns {boolean} */ export function isNumber(value) { const reg = /^[0-9]*$/ return reg.test(value) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是名称 * @param value * @returns {boolean} */ export function isName(value) { const reg = /^[\u4e00-\u9fa5a-zA-Z0-9]+$/ return reg.test(value) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否为IP * @param ip * @returns {boolean} */ export function isIP(ip) { const reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/ return reg.test(ip) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是传统网站 * @param url * @returns {boolean} */ export function isUrl(url) { const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ return reg.test(url) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是小写字母 * @param str * @returns {boolean} */ export function isLowerCase(str) { const reg = /^[a-z]+$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是大写字母 * @param str * @returns {boolean} */ export function isUpperCase(str) { const reg = /^[A-Z]+$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是大写字母开头 * @param str * @returns {boolean} */ export function isAlphabets(str) { const reg = /^[A-Za-z]+$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是字符串 * @param str * @returns {boolean} */ export function isString(str) { return typeof str === 'string' || str instanceof String } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是数组 * @param arg * @returns {arg is any[]|boolean} */ export function isArray(arg) { if (typeof Array.isArray === 'undefined') { return Object.prototype.toString.call(arg) === '[object Array]' } return Array.isArray(arg) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是端口号 * @param str * @returns {boolean} */ export function isPort(str) { const reg = /^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是手机号 * @param str * @returns {boolean} */ export function isPhone(str) { const reg = /^1\d{10}$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是身份证号(第二代) * @param str * @returns {boolean} */ export function isIdCard(str) { const reg = /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否是邮箱 * @param str * @returns {boolean} */ export function isEmail(str) { const reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否中文 * @param str * @returns {boolean} */ export function isChina(str) { const reg = /^[\u4E00-\u9FA5]{2,4}$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否为空 * @param str * @returns {boolean} */ export function isBlank(str) { return ( str == null || false || str === '' || str.trim() === '' || str.toLocaleLowerCase().trim() === 'null' ) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否为固话 * @param str * @returns {boolean} */ export function isTel(str) { const reg = /^(400|800)([0-9\\-]{7,10})|(([0-9]{4}|[0-9]{3})(-| )?)?([0-9]{7,8})((-| |转)*([0-9]{1,4}))?$/ return reg.test(str) } /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description 判断是否为数字且最多两位小数 * @param str * @returns {boolean} */ export function isNum(str) { const reg = /^\d+(\.\d{1,2})?$/ return reg.test(str) } ================================================ FILE: src/views/401.vue ================================================ ================================================ FILE: src/views/404.vue ================================================ ================================================ FILE: src/views/index/index.vue ================================================ ================================================ FILE: src/views/login/index.vue ================================================ ================================================ FILE: src/views/register/index.vue ================================================ ================================================ FILE: vab-icon/package.json ================================================ { "name": "vab-icon", "version": "0.0.1", "main": "lib/vab-icon.umd.min.js" } ================================================ FILE: vue.config.js ================================================ /** * @author chuzhixin 1204505056@qq.com (不想保留author可删除) * @description cli配置 */ const path = require('path') const { publicPath, assetsDir, outputDir, lintOnSave, transpileDependencies, title, abbreviation, devPort, providePlugin, build7z, donation, } = require('./src/config') const { webpackBarName, webpackBanner, donationConsole } = require('layouts') if (donation) donationConsole() const { version, author } = require('./package.json') const Webpack = require('webpack') const WebpackBar = require('webpackbar') const FileManagerPlugin = require('filemanager-webpack-plugin') const dayjs = require('dayjs') const date = dayjs().format('YYYY_M_D') const time = dayjs().format('YYYY-M-D HH:mm:ss') const productionGzipExtensions = ['html', 'js', 'css', 'svg'] process.env.VUE_APP_TITLE = title || 'vue-admin-better' process.env.VUE_APP_AUTHOR = author || 'chuzhixin 1204505056@qq.com' process.env.VUE_APP_UPDATE_TIME = time process.env.VUE_APP_VERSION = version const resolve = (dir) => path.join(__dirname, dir) const mockServer = () => { if (process.env.NODE_ENV === 'development') return require('./mock/mockServer.js') else return '' } module.exports = { publicPath, assetsDir, outputDir, lintOnSave, transpileDependencies, devServer: { hot: true, port: devPort, open: true, noInfo: false, overlay: { warnings: true, errors: true, }, after: mockServer(), }, configureWebpack() { return { resolve: { alias: { '@': resolve('src'), }, }, plugins: [ new Webpack.ProvidePlugin(providePlugin), new WebpackBar({ name: webpackBarName, }), ], } }, chainWebpack(config) { config.plugins.delete('preload') config.plugins.delete('prefetch') config.module .rule('svg') .exclude.add(resolve('src/remixIcon')) .add(resolve('src/colorfulIcon')) .end() config.module .rule('remixIcon') .test(/\.svg$/) .include.add(resolve('src/remixIcon')) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader') .options({ symbolId: 'remix-icon-[name]' }) .end() config.module .rule('colorfulIcon') .test(/\.svg$/) .include.add(resolve('src/colorfulIcon')) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader') .options({ symbolId: 'colorful-icon-[name]' }) .end() /* config.when(process.env.NODE_ENV === "development", (config) => { config.devtool("source-map"); }); */ config.when(process.env.NODE_ENV !== 'development', (config) => { config.performance.set('hints', false) config.devtool('none') config.optimization.splitChunks({ chunks: 'all', cacheGroups: { libs: { name: 'chunk-libs', test: /[\\/]node_modules[\\/]/, priority: 10, chunks: 'initial', }, elementUI: { name: 'chunk-elementUI', priority: 20, test: /[\\/]node_modules[\\/]_?element-ui(.*)/, }, fortawesome: { name: 'chunk-fortawesome', priority: 20, test: /[\\/]node_modules[\\/]_?@fortawesome(.*)/, }, }, }) config .plugin('banner') .use(Webpack.BannerPlugin, [`${webpackBanner}${time}`]) .end() config.module .rule('images') .use('image-webpack-loader') .loader('image-webpack-loader') .options({ bypassOnDebug: true, }) .end() }) if (build7z) { config.when(process.env.NODE_ENV === 'production', (config) => { config .plugin('fileManager') .use(FileManagerPlugin, [ { onEnd: { delete: [`./${outputDir}/video`, `./${outputDir}/data`], archive: [ { source: `./${outputDir}`, destination: `./${outputDir}/${abbreviation}_${outputDir}_${date}.7z`, }, ], }, }, ]) .end() }) } }, runtimeCompiler: true, productionSourceMap: false, css: { requireModuleExtension: true, sourceMap: true, loaderOptions: { scss: { /*sass-loader 8.0语法 */ //prependData: '@import "~@/styles/variables.scss";', /*sass-loader 9.0写法,感谢github用户 shaonialife*/ additionalData(content, loaderContext) { const { resourcePath, rootContext } = loaderContext const relativePath = path.relative(rootContext, resourcePath) if ( relativePath.replace(/\\/g, '/') !== 'src/styles/variables.scss' ) { return '@import "~@/styles/variables.scss";' + content } return content }, }, }, }, } ================================================ FILE: webstorm.config.js ================================================ 'use strict' const webpackConfig = require('@vue/cli-service/webpack.config.js') module.exports = webpackConfig