master 2d22a04f23e5 cached
114 files
281.4 KB
82.2k tokens
108 symbols
1 requests
Download .txt
Showing preview only (318K chars total). Download the full file or copy to clipboard to get everything.
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

<div align="center"><img width="200" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/logo/vab.png"/>
<h1> vue-admin-better(element-ui) </h1>
<p>The flying snow all over the sky is a flying note, playing out expectations with blessings. May the epidemic dissipate as soon as possible, may you no longer have regrets next year, may you be warm in winter, may you not be cold in spring, and may you have lights in the dark and an umbrella in the rain.
</p>
</div>

[![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.

<table>
<tr>
<!-- <td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/hbm.jpg">
</td> -->
<td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/zfb_kf.jpg">
</td>
<td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-2.jpg">
</td>
<td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-3.jpg">
</td>
</tr>
</table>

## 🙈 We promise to sponsor open source projects regularly (thank giant)

<a title="vue" href="https://opencollective.com/vuejs" target="_blank">
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/vue.png"/>
</a>
<a title="element-plus" href="https://opencollective.com/element-plus" target="_blank">
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/element-plus.png"/>
</a>
<a title="ant-design-vue" href="https://opencollective.com/ant-design-vue" target="_blank">
<img width="64px" src="https://images.opencollective.com/ant-design-vue/2ec179b/logo/256.png"/>
</a>

## 🎨 Acknowledge

| Project                                                          |
| ---------------------------------------------------------------- |
| [vue](https://github.com/vuejs/vue)                              |
| [element-ui](https://github.com/ElemeFE/element)                 |
| [element-plus](https://github.com/element-plus/element-plus)     |
| [ant-design-vue](https://github.com/vueComponent/ant-design-vue) |
| [mock](https://github.com/nuysoft/Mock)                          |
| [axios](https://github.com/axios/axios)                          |

## 👷 Outstanding contributors to the framework (in no order)

<a href="https://github.com/buuing" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/36689704?s=50"/>
</a>
<a href="https://github.com/hipi" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/22478003?s=50"/>
</a>
<a href="https://github.com/fwfmiao" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/29328241?s=50"/>
</a>
<a href="https://github.com/hdtopku" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/14859466?s=50"/>
</a>
<a href="https://github.com/shaonialife" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/16135960?s=50"/>
</a>

## 📌 Advantages and precautions

```
Compared with other open source admin frameworks, it has the following advantages:
1. Support the front-end control routing permission intelligence and the back-end control routing permission all mode
2. It is known that the open source Vue admin framework is the first to support the automatic generation and export function of mock
3. More than 50 global fine configurations are provided
4. Support SCSS automatic sorting and eslint automatic repair
5. Axios fine encapsulation supports multiple data sources, multiple successful code arrays, and application / JSON; charset=UTF-8、application/x-www-form-urlencoded; Charset = UTF-8 multiple parameter transfer modes
6. Support login RSA encryption
7. Support packaging to automatically generate 7z compressed packages
8. Support errorlog error interception
9. Support multi theme and multi layout switching
Precautions for use:
1. The project uses lf line feed instead of CRLF line feed by default. When creating a new file, please pay attention to selecting the file line feed
2. The project uses the strictest eslint verification specification (plugin: Vue / recommended) by default. Before using it, it is recommended to configure the development tool to realize automatic repair (vscode development is recommended)
3. The project uses the MIT open source agreement with the broadest requirements, and the MIT open source agreement can be used for free

```

## 💚 Suitable for people

- I am developing and want to use element UI / element plus, with 1 year of front-end development experience +.
- Familiar with vue.js technology stack and developed several practical projects with it.
- Students who are interested in principle and technology and want to improve.

## 🎉 Function map

![img](https://fastly.jsdelivr.net/gh/chuzhixin/image/vip/flow.drawio.png)

## 🗃️ design sketch

The following is a screenshot of the pro version:

<table>
<tr>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/2.png">
</td>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/6.png">
</td>
</tr>
<tr>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/8.png">
</td>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/9.png">
</td>
</tr>
<tr>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/3.png">
</td>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/5.png">
</td>
</tr>
</table>

## 📄 Commercial considerations

This project can be used for commercial purposes free of charge. Please abide by the MIT agreement and keep the author's technical support statement. For customized source code copyright information, please contact customer service QQ 783963206.

<!-- ## 严正声明

近期发现不少游手好闲之人有组织有预谋的利用码云、知乎、掘金等网站可用国外非法网站提供的匿名手机号注册的账号 bug 冒充 vab 去攻击 vue-element-admin,iview-admin,若依,d2-admin,ant-design-vue 的行为,恶意制造对立,试图让其他开源作者卷入其中,对各位开源作者造成的影响我们深表歉意,我们欢迎 vab 的用户去体验其他更优秀的框架,vue-admin-beautiful 走到今天实属不易,被人冒充,被人发帖诋毁,被人故意发布错误言论假装发帖表扬实则为我们招骂,无意动任何人的奶酪,从 2020 年至今坚持全职维护已过一年时间,说实在的我们靠技术生存并不丢人吧,一年来感谢 vab 的用户对我们不离不弃,也希望大家越来越好,加油! -->


================================================
FILE: README.md
================================================
<div style="filter: grayscale(100%)">

简体中文 | [English](./README.en.md)

<div align="center"><img width="200" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/logo/vab.png"/>
<h1> vue-admin-better</h1>

<p>众志成城,攻坚克难,愿所有美好纷沓而来!</p>
</div>

[![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 基础版本、开发工具自动配置教程及项目开发文档。
<table>
<tr>
<td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/zfb_kf.jpg">
</td>
<td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-2.jpg">
</td>
<td>
<img width="200px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/qq_group/vab-3.jpg">
</td>
</tr>
</table>

## 📦️ 桌面应用程序

- [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)

## 🙈 我们承诺将定期赞助的开源项目(感谢巨人)

<a title="vue" href="https://opencollective.com/vuejs" target="_blank">
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/vue.png"/>
</a>
<a title="element-plus" href="https://opencollective.com/element-plus" target="_blank">
<img width="64px" src="https://fastly.jsdelivr.net/gh/chuzhixin/image/element-plus.png"/>
</a>
<a title="ant-design-vue" href="https://opencollective.com/ant-design-vue" target="_blank">
<img width="64px" src="https://images.opencollective.com/ant-design-vue/2ec179b/logo/256.png"/>
</a>

## 🎨 鸣谢

| 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)      |

## 👷 框架杰出贡献者(排名不分先后)

<a href="https://github.com/buuing" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/36689704?s=50"/>
</a>
<a href="https://github.com/hipi" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/22478003?s=50"/>
</a>
<a href="https://github.com/fwfmiao" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/29328241?s=50"/>
</a>
<a href="https://github.com/hdtopku" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/14859466?s=50"/>
</a>
<a href="https://github.com/shaonialife" target="_blank">
<img width="50px" style="border-radius:999px" src="https://avatars.githubusercontent.com/u/16135960?s=50"/>
</a>

## 📌 优势及注意事项

```
对比其他开源 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 版的效果图展示:

<table>
<tr>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/2.png">
</td>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/6.png">
</td>
</tr>
<tr>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/8.png">
</td>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/9.png">
</td>
</tr>
<tr>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/3.png">
</td>
<td>
<img src="https://fastly.jsdelivr.net/gh/chuzhixin/image/5.png">
</td>
</tr>
</table>

## 📄 商用注意事项

此项目可免费用于商业用途,请遵守 MIT 协议并保留作者技术支持声明。

</div>


================================================
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
================================================
<template>
  <img
    v-if="isExternal"
    :src="styleExternalIcon"
    class="svg-external-icon svg-icon"
    v-on="$listeners"
  />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
  import { isExternal } from '@/utils/validate'

  export default {
    name: 'VabColorfulIcon',
    props: {
      iconClass: {
        type: String,
        required: true,
      },
      className: {
        type: String,
        default: '',
      },
    },
    computed: {
      isExternal() {
        return isExternal(this.iconClass)
      },
      iconName() {
        return `#colorful-icon-${this.iconClass}`
      },
      svgClass() {
        if (this.className) {
          return 'svg-icon ' + this.className
        } else {
          return 'svg-icon'
        }
      },
      styleExternalIcon() {
        return this.iconClass
      },
    },
  }
</script>

<style lang="scss" scoped>
  .svg-icon {
    width: 1em;
    height: 1em;
    overflow: hidden;
    vertical-align: -0.15em;
    fill: currentColor;

    &:hover {
      opacity: 0.8;
    }
  }

  .svg-external-icon {
    display: inline-block;
  }
</style>


================================================
FILE: layouts/VabErrorLog/index.vue
================================================
<template>
  <div v-if="errorLogs.length > 0">
    <el-badge
      :value="errorLogs.length"
      @click.native="dialogTableVisible = true"
    >
      <el-button type="danger">
        <vab-icon :icon="['fas', 'bug']" />
      </el-button>
    </el-badge>

    <el-dialog
      :visible.sync="dialogTableVisible"
      append-to-body
      width="70%"
      title="vue-admin-better异常捕获(温馨提示:错误必须解决)"
    >
      <el-table :data="errorLogs">
        <el-table-column label="报错路由">
          <template slot-scope="{ row }">
            <a :href="row.url" target="_blank">
              <el-tag type="success">{{ row.url }}</el-tag>
            </a>
          </template>
        </el-table-column>
        <el-table-column label="错误信息">
          <template slot-scope="{ row }">
            <el-tag type="danger">{{ decodeUnicode(row.err.message) }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="错误详情" width="120">
          <template slot-scope="scope">
            <el-popover placement="top-start" trigger="hover">
              <div style="color: red">
                {{ scope.row.err.stack }}
              </div>
              <el-button slot="reference">查看</el-button>
            </el-popover>
          </template>
        </el-table-column>
        <el-table-column width="380" label="操作">
          <template slot-scope="{ row }">
            <a
              v-for="(item, index) in searchList"
              :key="index"
              :href="item.url + decodeUnicode(row.err.message)"
              target="_blank"
            >
              <el-button style="margin-left: 5px" type="primary">
                <vab-icon :icon="['fas', 'search']" />
                {{ item.title }}
              </el-button>
            </a>
          </template>
        </el-table-column>
      </el-table>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogTableVisible = false">取 消</el-button>
        <el-button type="danger" icon="el-icon-delete" @click="clearAll">
          暂不显示
        </el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
  import { abbreviation, title } from '@/config'
  import { mapGetters } from 'vuex'

  export default {
    name: 'VabErrorLog',

    data() {
      return {
        dialogTableVisible: false,
        title: title,
        abbreviation: abbreviation,
        searchList: [
          {
            title: '百度搜索',
            url: 'https://www.baidu.com/baidu?wd=',
          },
          {
            title: '谷歌搜索',
            url: 'https://www.google.com/search?q=',
          },
          {
            title: 'Magi搜索',
            url: 'https://magi.com/search?q=',
          },
        ],
      }
    },

    computed: {
      ...mapGetters({
        errorLogs: 'errorLog/errorLogs',
      }),
    },
    methods: {
      clearAll() {
        this.dialogTableVisible = false
        this.$store.dispatch('errorLog/clearErrorLog')
      },
      decodeUnicode(str) {
        str = str.replace(/\\/g, '%')
        str = unescape(str)
        str = str.replace(/%/g, '\\')
        str = str.replace(/\\/g, '')
        return str
      },
    },
  }
</script>

<style lang="scss" scoped>
  ::v-deep {
    .el-badge {
      .el-button {
        display: flex;
        align-items: center;
        justify-items: center;
        height: 28px;
      }
    }
  }
</style>


================================================
FILE: layouts/VabFullScreenBar/index.vue
================================================
<template>
  <span :title="isFullscreen ? '退出全屏' : '进入全屏'">
    <vab-icon
      :icon="[
        'fas',
        isFullscreen ? 'compress-arrows-alt' : 'expand-arrows-alt',
      ]"
      @click="click"
    ></vab-icon>
  </span>
</template>

<script>
  import screenfull from 'screenfull'

  export default {
    name: 'VabFullScreenBar',
    data() {
      return {
        isFullscreen: false,
      }
    },
    mounted() {
      this.init()
    },
    beforeDestroy() {
      this.destroy()
    },
    methods: {
      click() {
        if (!screenfull.isEnabled) {
          this.$baseMessage('开启全屏失败', 'error')
          return false
        }
        screenfull.toggle()
        this.$emit('refresh')
      },
      change() {
        this.isFullscreen = screenfull.isFullscreen
      },
      init() {
        if (screenfull.isEnabled) {
          screenfull.on('change', this.change)
        }
      },
      destroy() {
        if (screenfull.isEnabled) {
          screenfull.off('change', this.change)
        }
      },
    },
  }
</script>


================================================
FILE: layouts/VabGithubCorner/index.vue
================================================
<template>
  <a
    href="https://github.com/chuzhixin/vue-admin-better"
    target="_blank"
    class="github-corner"
    aria-label="View source on Github"
  >
    <svg
      width="80"
      height="80"
      viewBox="0 0 250 250"
      class="github-color"
      aria-hidden="true"
    >
      <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
      <path
        d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
        fill="currentColor"
        style="transform-origin: 130px 106px"
        class="octo-arm"
      />
      <path
        d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
        fill="currentColor"
        class="octo-body"
      />
    </svg>
  </a>
</template>
<script>
  export default {
    name: 'VabGithubCorner',
  }
</script>

<style lang="scss" scoped>
  .github-corner {
    position: absolute;
    top: 0;
    right: 0;
    z-index: $base-z-index - 3;

    .octo-arm {
      animation: octocat-wave 560ms ease-in-out infinite;
    }

    &:hover {
      .octo-arm {
        animation: octocat-wave 560ms ease-in-out infinite;
      }
    }

    .github-color {
      color: #fff;
      fill: $base-color-blue;
    }
  }

  @keyframes octocat-wave {
    0%,
    100% {
      transform: rotate(0);
    }

    20%,
    60% {
      transform: rotate(-25deg);
    }

    40%,
    80% {
      transform: rotate(100deg);
    }
  }
</style>


================================================
FILE: layouts/VabQueryForm/VabQueryFormBottomPanel.vue
================================================
<template>
  <el-col :span="24">
    <div class="bottom-panel">
      <slot></slot>
    </div>
  </el-col>
</template>

<script>
  export default {
    name: "VabQueryFormBottomPanel",
    props: {},
    data() {
      return {};
    },
    created() {},
    mounted() {},
    methods: {},
  };
</script>


================================================
FILE: layouts/VabQueryForm/VabQueryFormLeftPanel.vue
================================================
<template>
  <el-col :xs="24" :sm="24" :md="24" :lg="span" :xl="span">
    <div class="left-panel">
      <slot></slot>
    </div>
  </el-col>
</template>

<script>
  export default {
    name: 'VabQueryFormLeftPanel',
    props: {
      span: {
        type: Number,
        default: 14,
      },
    },
    data() {
      return {}
    },
    created() {},
    mounted() {},
    methods: {},
  }
</script>


================================================
FILE: layouts/VabQueryForm/VabQueryFormRightPanel.vue
================================================
<template>
  <el-col :xs="24" :sm="24" :md="24" :lg="span" :xl="span">
    <div class="right-panel">
      <slot></slot>
    </div>
  </el-col>
</template>

<script>
  export default {
    name: 'VabQueryFormRightPanel',
    props: {
      span: {
        type: Number,
        default: 10,
      },
    },
    data() {
      return {}
    },
    created() {},
    mounted() {},
    methods: {},
  }
</script>


================================================
FILE: layouts/VabQueryForm/VabQueryFormTopPanel.vue
================================================
<template>
  <el-col :span="24">
    <div class="top-panel">
      <slot></slot>
    </div>
  </el-col>
</template>

<script>
  export default {
    name: 'VabQueryFormTopPanel',
    props: {},
    data() {
      return {}
    },
    created() {},
    mounted() {},
    methods: {},
  }
</script>


================================================
FILE: layouts/VabQueryForm/index.vue
================================================
<template>
  <el-row :gutter="0" class="vab-query-form">
    <slot></slot>
  </el-row>
</template>

<script>
  export default {
    name: 'VabQueryForm',
    props: {},
    data() {
      return {}
    },
    created() {},
    mounted() {},
    methods: {},
  }
</script>

<style lang="scss" scoped>
  @mixin panel {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
  }

  .vab-query-form {
    margin-bottom: 10px;

    ::v-deep {
      .top-panel {
        @include panel;
      }

      .bottom-panel {
        @include panel;

        padding-top: 14px;
        border-top: 1px solid #dcdfe6;
      }

      .left-panel {
        @include panel;

        > .el-button,
        .el-form-item {
          margin: 5px;
        }
      }

      .right-panel {
        @include panel;

        justify-content: flex-end;

        .el-form-item {
          margin: 5px;
        }
      }
    }
  }
</style>


================================================
FILE: layouts/VabRemixIcon/index.vue
================================================
<template>
  <div
    v-if="isExternal"
    :style="styleExternalIcon"
    class="svg-external-icon svg-icon"
    v-on="$listeners"
  />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
  import { isExternal } from '@/utils/validate'

  export default {
    name: 'VabRemixIcon',
    props: {
      iconClass: {
        type: String,
        required: true,
      },
      className: {
        type: String,
        default: '',
      },
    },
    computed: {
      isExternal() {
        return isExternal(this.iconClass)
      },
      iconName() {
        return `#remix-icon-${this.iconClass}`
      },
      svgClass() {
        if (this.className) {
          return 'svg-icon ' + this.className
        } else {
          return 'svg-icon'
        }
      },
      styleExternalIcon() {
        return {
          mask: `url(${this.iconClass}) no-repeat 50% 50%`,
          '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`,
        }
      },
    },
  }
</script>

<style lang="scss" scoped>
  .svg-icon {
    width: 1.125em;
    height: 1.125em;
    overflow: hidden;
    fill: currentColor;

    &:hover {
      opacity: 0.8;
    }
  }

  .svg-external-icon {
    display: inline-block;
    background-color: currentColor;
    mask-size: cover !important;
  }
</style>


================================================
FILE: layouts/VabSideBar/components/VabMenuItem.vue
================================================
<template>
  <el-menu-item :index="handlePath(routeChildren.path)" @click="handleLink">
    <vab-icon
      v-if="routeChildren.meta.icon"
      :icon="['fas', routeChildren.meta.icon]"
      class="vab-fas-icon"
    />
    <span>{{ routeChildren.meta.title }}</span>
    <el-tag
      v-if="routeChildren.meta && routeChildren.meta.badge"
      type="danger"
      effect="dark"
    >
      {{ routeChildren.meta.badge }}
    </el-tag>
  </el-menu-item>
</template>

<script>
  import { isExternal } from '@/utils/validate'
  import path from 'path'

  export default {
    name: 'VabMenuItem',
    props: {
      routeChildren: {
        type: Object,
        default() {
          return null
        },
      },
      item: {
        type: Object,
        default() {
          return null
        },
      },
      fullPath: {
        type: String,
        default: '',
      },
    },
    methods: {
      handlePath(routePath) {
        if (isExternal(routePath)) {
          return routePath
        }
        if (isExternal(this.fullPath)) {
          return this.fullPath
        }
        return path.resolve(this.fullPath, routePath)
      },
      handleLink() {
        const routePath = this.routeChildren.path
        const target = this.routeChildren.meta.target

        if (target === '_blank') {
          if (isExternal(routePath)) {
            window.open(routePath)
          } else if (isExternal(this.fullPath)) {
            window.open(this.fullPath)
          } else if (
            this.$route.path !== path.resolve(this.fullPath, routePath)
          ) {
            let routeData = this.$router.resolve(
              path.resolve(this.fullPath, routePath)
            )
            window.open(routeData.href)
          }
        } else {
          if (isExternal(routePath)) {
            window.location.href = routePath
          } else if (isExternal(this.fullPath)) {
            window.location.href = this.fullPath
          } else if (
            this.$route.path !== path.resolve(this.fullPath, routePath)
          ) {
            this.$router.push(path.resolve(this.fullPath, routePath))
          }
        }
      },
    },
  }
</script>


================================================
FILE: layouts/VabSideBar/components/VabSideBarItem.vue
================================================
<template>
  <component
    :is="menuComponent"
    v-if="!item.hidden"
    :item="item"
    :full-path="fullPath"
    :route-children="routeChildren"
  >
    <template v-if="item.children && item.children.length">
      <vab-side-bar-item
        v-for="route in item.children"
        :key="route.path"
        :full-path="handlePath(route.path)"
        :item="route"
      />
    </template>
  </component>
</template>

<script>
  import { isExternal } from '@/utils/validate'
  import path from 'path'

  export default {
    name: 'VabSideBarItem',
    props: {
      item: {
        type: Object,
        required: true,
      },
      fullPath: {
        type: String,
        default: '',
      },
    },
    data() {
      this.onlyOneChild = null
      return {}
    },
    computed: {
      menuComponent() {
        if (
          this.handleChildren(this.item.children, this.item) &&
          (!this.routeChildren.children ||
            this.routeChildren.notShowChildren) &&
          !this.item.alwaysShow
        ) {
          return 'VabMenuItem'
        } else {
          return 'VabSubmenu'
        }
      },
    },
    methods: {
      handleChildren(children = [], parent) {
        if (children === null) children = []
        const showChildren = children.filter((item) => {
          if (item.hidden) {
            return false
          } else {
            this.routeChildren = item
            return true
          }
        })
        if (showChildren.length === 1) {
          return true
        }

        if (showChildren.length === 0) {
          this.routeChildren = {
            ...parent,
            path: '',
            notShowChildren: true,
          }
          return true
        }
        return false
      },
      handlePath(routePath) {
        if (isExternal(routePath)) {
          return routePath
        }
        if (isExternal(this.fullPath)) {
          return this.fullPath
        }
        return path.resolve(this.fullPath, routePath)
      },
    },
  }
</script>

<style lang="scss" scoped>
  .vab-nav-icon {
    margin-right: 4px;
  }

  ::v-deep {
    .el-tag {
      float: right;
      height: 16px;
      padding-right: 4px;
      padding-left: 4px;
      margin-top: calc((#{$base-menu-item-height} - 16px) / 2);
      line-height: 16px;
      border: 0;
    }
  }
</style>


================================================
FILE: layouts/VabSideBar/components/VabSubmenu.vue
================================================
<template>
  <el-submenu
    ref="subMenu"
    :index="handlePath(item.path)"
    :popper-append-to-body="false"
  >
    <template slot="title">
      <vab-icon
        v-if="item.meta && item.meta.icon"
        :icon="['fas', item.meta.icon]"
        class="vab-fas-icon"
      />
      <vab-remix-icon
        v-if="item.meta && item.meta.remixIcon"
        :icon-class="item.meta.remixIcon"
        class="vab-remix-icon"
      />
      <span>{{ item.meta.title }}</span>
    </template>
    <slot />
  </el-submenu>
</template>

<script>
  import { isExternal } from '@/utils/validate'
  import path from 'path'

  export default {
    name: 'VabSubmenu',
    props: {
      routeChildren: {
        type: Object,
        default() {
          return null
        },
      },
      item: {
        type: Object,
        default() {
          return null
        },
      },
      fullPath: {
        type: String,
        default: '',
      },
    },
    methods: {
      handlePath(routePath) {
        if (isExternal(routePath)) {
          return routePath
        }
        if (isExternal(this.fullPath)) {
          return this.fullPath
        }
        return path.resolve(this.fullPath, routePath)
      },
    },
  }
</script>


================================================
FILE: layouts/VabSideBar/index.vue
================================================
<template>
  <el-scrollbar class="side-bar-container" :class="{ 'is-collapse': collapse }">
    <vab-logo />
    <el-menu
      :background-color="variables['menu-background']"
      :text-color="variables['menu-color']"
      :active-text-color="variables['menu-color-active']"
      :default-active="activeMenu"
      :collapse="collapse"
      :collapse-transition="false"
      :default-openeds="defaultOpens"
      :unique-opened="uniqueOpened"
      mode="vertical"
    >
      <template v-for="route in routes">
        <vab-side-bar-item
          :key="route.path"
          :full-path="route.path"
          :item="route"
        />
      </template>
    </el-menu>
  </el-scrollbar>
</template>
<script>
  import variables from '@/styles/variables.scss'
  import { mapGetters } from 'vuex'
  import { defaultOopeneds, uniqueOpened } from '@/config'

  export default {
    name: 'VabSideBar',
    data() {
      return {
        uniqueOpened,
      }
    },
    computed: {
      ...mapGetters({
        collapse: 'settings/collapse',
        routes: 'routes/routes',
      }),
      defaultOpens() {
        if (this.collapse) {
        }
        return defaultOopeneds
      },
      activeMenu() {
        const route = this.$route
        const { meta, path } = route
        if (meta.activeMenu) {
          return meta.activeMenu
        }
        return path
      },
      variables() {
        return variables
      },
    },
  }
</script>
<style lang="scss" scoped>
  @mixin active {
    &:hover {
      color: $base-color-white;
      background-color: $base-menu-background-active !important;
    }

    &.is-active {
      color: $base-color-white;
      background-color: $base-menu-background-active !important;
    }
  }

  .side-bar-container {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: $base-z-index;
    width: $base-left-menu-width;
    height: 100vh;
    overflow: hidden;
    background: $base-menu-background;
    box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35);
    transition: width $base-transition-time;

    &.is-collapse {
      width: $base-left-menu-width-min;
      border-right: 0;

      ::v-deep {
        .el-menu {
          transition: width $base-transition-time;
        }

        .el-menu--collapse {
          border-right: 0;

          .el-submenu__icon-arrow {
            right: 10px;
            margin-top: -3px;
          }
          .el-menu-item,
          .el-submenu {
            text-align: center;
          }
        }
      }
    }

    ::v-deep {
      .el-scrollbar__wrap {
        overflow-x: hidden;
      }

      .el-menu {
        border: 0;

        .vab-fas-icon {
          padding-right: 3px;
          font-size: $base-font-size-default;
          display: inline-block;
          width: 14px;
        }

        .vab-remix-icon {
          padding-right: 3px;
          font-size: $base-font-size-default + 2;
        }
      }

      .el-menu-item,
      .el-submenu__title {
        height: $base-menu-item-height;
        line-height: $base-menu-item-height;
        vertical-align: middle;
      }

      .el-menu-item {
        @include active;
      }
    }
  }
</style>


================================================
FILE: layouts/VabTabsBar/index.vue
================================================
<template>
  <div id="tabs-bar-container" class="tabs-bar-container">
    <el-tabs
      v-model="tabActive"
      type="card"
      class="tabs-content"
      @tab-click="handleTabClick"
      @tab-remove="handleTabRemove"
    >
      <el-tab-pane
        v-for="item in visitedRoutes"
        :key="item.path"
        :label="item.meta.title"
        :name="item.path"
        :closable="!isAffix(item)"
      ></el-tab-pane>
    </el-tabs>

    <el-dropdown @command="handleCommand">
      <span style="cursor: pointer">
        更多操作
        <i class="el-icon-arrow-down el-icon--right"></i>
      </span>
      <el-dropdown-menu slot="dropdown" class="tabs-more">
        <el-dropdown-item command="closeOtherstabs">
          <vab-icon :icon="['fas', 'times-circle']" />
          关闭其他
        </el-dropdown-item>
        <el-dropdown-item command="closeLefttabs">
          <vab-icon :icon="['fas', 'arrow-alt-circle-left']"></vab-icon>
          关闭左侧
        </el-dropdown-item>
        <el-dropdown-item command="closeRighttabs">
          <vab-icon :icon="['fas', 'arrow-alt-circle-right']"></vab-icon>
          关闭右侧
        </el-dropdown-item>
        <el-dropdown-item command="closeAlltabs">
          <vab-icon :icon="['fas', 'ban']"></vab-icon>
          关闭全部
        </el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>

<script>
  import path from 'path'
  import { mapGetters } from 'vuex'

  export default {
    name: 'VabTabsBar',
    data() {
      return {
        affixtabs: [],
        tabActive: '',
      }
    },

    computed: {
      ...mapGetters({
        visitedRoutes: 'tabsBar/visitedRoutes',
        routes: 'routes/routes',
      }),
    },
    watch: {
      $route: {
        handler(route) {
          this.inittabs()
          this.addtabs()
          let tabActive = ''
          this.visitedRoutes.forEach((item, index) => {
            if (item.path === this.$route.path) {
              tabActive = item.path
            }
          })
          this.tabActive = tabActive
        },
        immediate: true,
      },
    },
    mounted() {
      //console.log(this.visitedRoutes);
    },
    methods: {
      async handleTabRemove(tabActive) {
        let view
        this.visitedRoutes.forEach((item, index) => {
          if (tabActive == item.path) {
            view = item
          }
        })
        const { visitedRoutes } = await this.$store.dispatch(
          'tabsBar/delRoute',
          view
        )
        if (this.isActive(view)) {
          this.toLastTag(visitedRoutes, view)
        }
      },
      handleTabClick(tab) {
        const route = this.visitedRoutes.filter((item, index) => {
          if (tab.index == index) return item
        })[0]
        if (this.$route.path !== route.path) {
          this.$router.push({
            path: route.path,
            query: route.query,
            fullPath: route.fullPath,
          })
        } else {
          return false
        }
      },
      isActive(route) {
        return route.path === this.$route.path
      },
      isAffix(tag) {
        return tag.meta && tag.meta.affix
      },
      filterAffixtabs(routes, basePath = '/') {
        let tabs = []
        routes.forEach((route) => {
          if (route.meta && route.meta.affix) {
            const tagPath = path.resolve(basePath, route.path)
            tabs.push({
              fullPath: tagPath,
              path: tagPath,
              name: route.name,
              meta: { ...route.meta },
            })
          }
          if (route.children) {
            const temptabs = this.filterAffixtabs(route.children, route.path)
            if (temptabs.length >= 1) {
              tabs = [...tabs, ...temptabs]
            }
          }
        })
        return tabs
      },
      inittabs() {
        const affixtabs = (this.affixtabs = this.filterAffixtabs(this.routes))
        for (const tag of affixtabs) {
          if (tag.name) {
            this.$store.dispatch('tabsBar/addVisitedRoute', tag)
          }
        }
      },
      addtabs() {
        const { name } = this.$route
        if (name) {
          this.$store.dispatch('tabsBar/addVisitedRoute', this.$route)
        }
        return false
      },
      handleCommand(command) {
        switch (command) {
          case 'refreshRoute':
            this.refreshRoute()
            break
          case 'closeOtherstabs':
            this.closeOtherstabs()
            break
          case 'closeLefttabs':
            this.closeLefttabs()
            break
          case 'closeRighttabs':
            this.closeRighttabs()
            break
          case 'closeAlltabs':
            this.closeAlltabs()
            break
        }
      },
      async refreshRoute() {
        this.$baseEventBus.$emit('reloadrouter-view')
      },
      async closeSelectedTag(view) {
        const { visitedRoutes } = await this.$store.dispatch(
          'tabsBar/delRoute',
          view
        )
        if (this.isActive(view)) {
          this.toLastTag(visitedRoutes, view)
        }
      },
      async closeOtherstabs() {
        const view = await this.toThisTag()
        await this.$store.dispatch('tabsBar/delOthersRoutes', view)
      },
      async closeLefttabs() {
        const view = await this.toThisTag()
        await this.$store.dispatch('tabsBar/delLeftRoutes', view)
      },
      async closeRighttabs() {
        const view = await this.toThisTag()
        await this.$store.dispatch('tabsBar/delRightRoutes', view)
      },
      async closeAlltabs() {
        const view = await this.toThisTag()
        const { visitedRoutes } = await this.$store.dispatch(
          'tabsBar/delAllRoutes'
        )
        if (this.affixtabs.some((tag) => tag.path === view.path)) {
          return
        }
        this.toLastTag(visitedRoutes, view)
      },
      toLastTag(visitedRoutes, view) {
        const latestView = visitedRoutes.slice(-1)[0]
        if (latestView) {
          this.$router.push(latestView)
        } else {
          this.$router.push('/')
        }
      },
      async toThisTag() {
        const view = this.visitedRoutes.filter((item, index) => {
          if (item.path === this.$route.fullPath) {
            return item
          }
        })[0]
        if (this.$route.path !== view.path) this.$router.push(view)
        return view
      },
    },
  }
</script>

<style lang="scss" scoped>
  .tabs-bar-container {
    position: relative;
    box-sizing: border-box;
    display: flex;
    align-content: center;
    align-items: center;
    justify-content: space-between;
    height: $base-tabs-bar-height;
    padding-right: $base-padding;
    padding-left: $base-padding;
    user-select: none;
    background: $base-color-white;
    border-top: 1px solid #f6f6f6;

    ::v-deep {
      .fold-unfold {
        margin-right: $base-padding;
      }
    }

    .tabs-content {
      width: calc(100% - 90px);
      height: $base-tag-item-height;

      ::v-deep {
        .el-tabs__nav-next,
        .el-tabs__nav-prev {
          height: $base-tag-item-height;
          line-height: $base-tag-item-height;
        }

        .el-tabs__header {
          border-bottom: 0;

          .el-tabs__nav {
            border: 0;
          }

          .el-tabs__item {
            box-sizing: border-box;
            height: $base-tag-item-height;
            margin-right: 5px;
            line-height: $base-tag-item-height;
            border: 1px solid $base-border-color;
            border-radius: $base-border-radius;
            transition: padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;

            &.is-active {
              border: 1px solid $base-color-blue;
            }
          }
        }
      }
    }

    .more {
      display: flex;
      align-content: center;
      align-items: center;
      cursor: pointer;
    }
  }
</style>


================================================
FILE: layouts/VabTopBar/index.vue
================================================
<template>
  <div class="top-bar-container">
    <div class="vab-main">
      <el-row>
        <el-col :xl="7" :lg="7" :md="7" :sm="7" :xs="7">
          <vab-logo />
        </el-col>
        <el-col :xl="12" :lg="12" :md="12" :sm="12" :xs="12">
          <el-menu
            :background-color="variables['menu-background']"
            :text-color="variables['menu-color']"
            :active-text-color="variables['menu-color-active']"
            :default-active="activeMenu"
            mode="horizontal"
            menu-trigger="hover"
          >
            <template v-for="route in routes">
              <vab-side-bar-item
                v-if="!route.hidden"
                :key="route.path"
                :full-path="route.path"
                :item="route"
              />
            </template>
          </el-menu>
        </el-col>
        <el-col :xl="5" :lg="5" :md="5" :sm="5" :xs="5">
          <div class="right-panel">
            <vab-error-log />
            <vab-full-screen-bar @refresh="refreshRoute" />
            <vab-theme-bar class="hidden-md-and-down" />
            <vab-icon
              title="重载路由"
              :pulse="pulse"
              :icon="['fas', 'redo']"
              @click="refreshRoute"
            />
            <vab-avatar />
          </div>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
  import variables from '@/styles/variables.scss'
  import { mapGetters } from 'vuex'

  export default {
    name: 'VabTopBar',
    data() {
      return {
        pulse: false,
        menuTrigger: 'hover',
      }
    },
    computed: {
      ...mapGetters({
        routes: 'routes/routes',
        visitedRoutes: 'tabsBar/visitedRoutes',
      }),
      activeMenu() {
        const route = this.$route
        const { meta, path } = route
        if (meta.activeMenu) {
          return meta.activeMenu
        }
        return path
      },
      variables() {
        return variables
      },
    },
    methods: {
      async refreshRoute() {
        this.$baseEventBus.$emit('reload-router-view')
        this.pulse = true
        setTimeout(() => {
          this.pulse = false
        }, 1000)
      },
    },
  }
</script>
<style lang="scss" scoped>
  .top-bar-container {
    display: flex;
    align-items: center;
    justify-items: flex-end;
    height: $base-top-bar-height;
    background: $base-menu-background;

    .vab-main {
      background: $base-menu-background;

      ::v-deep {
        .el-menu {
          &.el-menu--horizontal {
            display: flex;
            align-items: center;
            justify-content: flex-end;
            height: $base-top-bar-height;
            border-bottom: 0 solid transparent !important;

            .el-menu-item,
            .el-submenu__title {
              padding: 0 15px;
            }

            @media only screen and (max-width: 767px) {
              .el-menu-item,
              .el-submenu__title {
                padding: 0 8px;
              }

              li:nth-child(4),
              li:nth-child(5) {
                display: none !important;
              }
            }

            > .el-menu-item {
              height: $base-top-bar-height;
              line-height: $base-top-bar-height;
            }

            > .el-submenu {
              .el-submenu__title {
                height: $base-top-bar-height;
                line-height: $base-top-bar-height;
              }
            }
          }

          svg {
            width: 1rem;
            margin-right: 3px;
          }

          &--horizontal {
            .el-menu {
              .el-menu-item,
              .el-submenu__title {
                height: $base-menu-item-height;
                line-height: $base-menu-item-height;
              }
            }

            .el-submenu,
            .el-menu-item {
              &.is-active {
                background-color: $base-color-blue !important;
                border-bottom: 0 solid transparent !important;

                .el-submenu__title {
                  border-bottom: 0 solid transparent !important;
                }
              }
            }

            > .el-menu-item {
              .el-tag {
                margin-top: calc(#{$base-top-bar-height} / 2 - 7.5px);
                margin-left: 5px;
              }

              @media only screen and (max-width: 1199px) {
                .el-tag {
                  display: none;
                }
              }

              &.is-active {
                background-color: transparent !important;
                border-bottom: 3px solid $base-color-blue !important;
              }
            }
          }
        }
      }
    }

    .right-panel {
      display: flex;
      align-items: center;
      justify-content: flex-end;
      height: $base-top-bar-height;

      ::v-deep {
        .user-name {
          color: rgba($base-color-white, 0.9);
        }

        .user-name + i {
          color: rgba($base-color-white, 0.9);
        }

        svg {
          width: 1em;
          height: 1em;
          margin-right: 15px;
          font-size: $base-font-size-big;
          color: rgba($base-color-white, 0.9);
          cursor: pointer;
          fill: rgba($base-color-white, 0.9);
        }

        button {
          svg {
            margin-right: 0;
            color: rgba($base-color-white, 0.9);
            cursor: pointer;
            fill: rgba($base-color-white, 0.9);
          }
        }

        .el-badge {
          margin-right: 15px;
        }
      }
    }
  }
</style>


================================================
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
================================================
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <title><%= VUE_APP_TITLE %></title>
    <meta
      name="keywords"
      content="vab,vab官网,后台管理框架,vue后台管理框架,vue-admin-better,vue-admin-better官网,vue-admin-better文档,vue-element-admin,vue-element-admin官网,vue-element-admin文档,vue-admin,vue-admin官网,vue-admin文档"
    />
    <meta
      name="description"
      content="<%= VUE_APP_TITLE %>官网与文档基于vue-admin-better构建,简称vab(是一款超棒的vue+element中后台前端快速开发框架),QQ群972435319,作者:<%= VUE_APP_AUTHOR %>"
    />
    <meta name="author" content="<%= VUE_APP_AUTHOR %>" />
    <link rel="stylesheet" href="<%= BASE_URL %>static/css/loading.css" />
    <script>
      var _hmt = _hmt || []
      ;(function () {
        var hm = document.createElement('script')
        hm.src = 'https://hm.baidu.com/hm.js?7174bade1219f9cc272e7978f9523fc8'
        var s = document.getElementsByTagName('script')[0]
        s.parentNode.insertBefore(hm, s)
      })()
    </script>
  </head>
  <body>
    <noscript>
      非常抱歉鉴于安全考量,您无法查看<%= VUE_APP_TITLE %>
      源代码,该系统基于vue-admin-better开发
    </noscript>
    <div id="vue-admin-better">
      <div class="first-loading-wrp">
        <div class="loading-wrp">
          <span class="dot dot-spin">
            <i></i>
            <i></i>
            <i></i>
            <i></i>
          </span>
        </div>
        <h1><%= VUE_APP_TITLE %></h1>
      </div>
    </div>
    <script>
      ;/^http(s*):\/\//.test(location.href) ||
        alert('基于vue-admin-better开发的项目需要部署到服务器下访问')
    </script>
  </body>
</html>


================================================
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
================================================
<template>
  <div id="vue-admin-better">
    <router-view />
  </div>
</template>

<script>
  export default {
    name: 'App',
    mounted() {},
  }
</script>


================================================
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
================================================
<template>
  <router-view />
</template>


================================================
FILE: src/layouts/components/VabAd/index.vue
================================================
<template>
  <div class="vab-ad">
    <el-carousel
      v-if="adList"
      height="30px"
      direction="vertical"
      :autoplay="true"
      :interval="3000"
      indicator-position="none"
    >
      <el-carousel-item v-for="(item, index) in adList" :key="index">
        <el-tag type="warning">Ad</el-tag>
        <a target="_blank" :href="item.url">{{ item.title }}</a>
      </el-carousel-item>
    </el-carousel>
  </div>
</template>
<script>
  import { getList } from '@/api/ad'
  export default {
    name: 'VabAd',
    data() {
      return {
        nodeEnv: process.env.NODE_ENV,
        adList: [],
      }
    },
    created() {
      this.fetchData()
    },
    methods: {
      async fetchData() {
        const { data } = await getList()
        this.adList = data
      },
    },
  }
</script>
<style lang="scss" scoped>
  .vab-ad {
    height: 30px;
    padding-right: $base-padding;
    padding-left: $base-padding;
    margin-bottom: -20px;
    line-height: 30px;
    cursor: pointer;

    a {
      color: #999;
    }
  }
</style>


================================================
FILE: src/layouts/components/VabAppMain/index.vue
================================================
<template>
  <div v-if="routerView" class="app-main-container">
    <vab-github-corner />
    <transition mode="out-in" name="fade-transform">
      <keep-alive :include="cachedRoutes" :max="keepAliveMaxNum">
        <router-view :key="key" class="app-main-height" />
      </keep-alive>
    </transition>
    <footer v-show="footerCopyright" class="footer-copyright">
      Copyright
      <vab-icon :icon="['fas', 'copyright']"></vab-icon>
      vue-admin-better-pro 开源免费版 {{ fullYear }}
    </footer>
  </div>
</template>

<script>
  import { mapActions, mapGetters } from 'vuex'
  import { copyright, footerCopyright, keepAliveMaxNum, title } from '@/config'

  export default {
    name: 'VabAppMain',
    data() {
      return {
        show: false,
        fullYear: new Date().getFullYear(),
        copyright,
        title,
        keepAliveMaxNum,
        routerView: true,
        footerCopyright,
      }
    },
    computed: {
      ...mapGetters({
        visitedRoutes: 'tabsBar/visitedRoutes',
        device: 'settings/device',
      }),
      cachedRoutes() {
        const cachedRoutesArr = []
        this.visitedRoutes.forEach((item) => {
          if (!item.meta.noKeepAlive) {
            cachedRoutesArr.push(item.name)
          }
        })
        return cachedRoutesArr
      },
      key() {
        return this.$route.path
      },
    },
    watch: {
      $route: {
        handler(route) {
          if ('mobile' === this.device) this.foldSideBar()
        },
        immediate: true,
      },
    },
    created() {
      //重载所有路由
      this.$baseEventBus.$on('reload-router-view', () => {
        this.routerView = false
        this.$nextTick(() => {
          this.routerView = true
        })
      })
    },
    mounted() {},
    methods: {
      ...mapActions({
        foldSideBar: 'settings/foldSideBar',
      }),
    },
  }
</script>

<style lang="scss" scoped>
  .app-main-container {
    position: relative;
    width: 100%;
    overflow: hidden;
    .vab-keel {
      margin: $base-padding;
    }
    .app-main-height {
      min-height: $base-app-main-height;
    }

    .footer-copyright {
      min-height: 55px;
      line-height: 55px;
      color: rgba(0, 0, 0, 0.45);
      text-align: center;
      border-top: 1px dashed $base-border-color;
    }
  }
</style>


================================================
FILE: src/layouts/components/VabAvatar/index.vue
================================================
<template>
  <el-dropdown @command="handleCommand">
    <span class="avatar-dropdown">
      <!--<el-avatar class="user-avatar" :src="avatar"></el-avatar>-->
      <img class="user-avatar" :src="avatar" alt="" />
      <div class="user-name">
        {{ username }}
        <i class="el-icon-arrow-down el-icon--right"></i>
      </div>
    </span>

    <el-dropdown-menu slot="dropdown">
      <el-dropdown-item command="github">github地址</el-dropdown-item>
      <el-dropdown-item command="gitee" divided>码云地址</el-dropdown-item>
      <el-dropdown-item command="pro" divided>pro付费版地址</el-dropdown-item>
      <el-dropdown-item command="plus" divided>plus付费版地址</el-dropdown-item>
      <el-dropdown-item command="shop" divided>
        shop-vite付费版地址
      </el-dropdown-item>
      <el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
    </el-dropdown-menu>
  </el-dropdown>
</template>

<script>
  import { mapGetters } from 'vuex'
  import { recordRoute } from '@/config'

  export default {
    name: 'VabAvatar',
    computed: {
      ...mapGetters({
        avatar: 'user/avatar',
        username: 'user/username',
      }),
    },
    methods: {
      handleCommand(command) {
        switch (command) {
          case 'logout':
            this.logout()
            break
          case 'personalCenter':
            this.personalCenter()
            break
          case 'github':
            window.open('https://github.com/chuzhixin/vue-admin-better')
            break
          case 'gitee':
            window.open('https://gitee.com/chu1204505056/vue-admin-better')
            break
          case 'pro':
            window.open(
              'https://vue-admin-beautiful.com/admin-pro/?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci='
            )
            break
          case 'plus':
            window.open(
              'https://vue-admin-beautiful.com/admin-plus/?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci='
            )
          case 'shop':
            window.open(
              'https://vue-admin-beautiful.com/shop-vite/?hmsr=homeAd&hmpl=&hmcu=&hmkw=&hmci='
            )
        }
      },
      personalCenter() {
        this.$router.push('/personalCenter/personalCenter')
      },
      logout() {
        this.$baseConfirm(
          '您确定要退出' + this.$baseTitle + '吗?',
          null,
          async () => {
            await this.$store.dispatch('user/logout')
            if (recordRoute) {
              const fullPath = this.$route.fullPath
              this.$router.push(`/login?redirect=${fullPath}`)
            } else {
              this.$router.push('/login')
            }
          }
        )
      },
    },
  }
</script>
<style lang="scss" scoped>
  .avatar-dropdown {
    display: flex;
    align-content: center;
    align-items: center;
    justify-content: center;
    justify-items: center;
    height: 50px;
    padding: 0;

    .user-avatar {
      width: 40px;
      height: 40px;
      cursor: pointer;
      border-radius: 50%;
    }

    .user-name {
      position: relative;
      margin-left: 5px;
      margin-left: 5px;
      cursor: pointer;
    }
  }
</style>


================================================
FILE: src/layouts/components/VabBreadcrumb/index.vue
================================================
<template>
  <el-breadcrumb class="breadcrumb-container" separator=">">
    <el-breadcrumb-item v-for="item in list" :key="item.path">
      {{ item.meta.title }}
    </el-breadcrumb-item>
  </el-breadcrumb>
</template>

<script>
  export default {
    name: 'VabBreadcrumb',
    data() {
      return {
        list: this.getBreadcrumb(),
      }
    },
    watch: {
      $route() {
        this.list = this.getBreadcrumb()
      },
    },
    methods: {
      getBreadcrumb() {
        return this.$route.matched.filter(
          (item) => item.name && item.meta.title
        )
      },
    },
  }
</script>

<style lang="scss" scoped>
  .breadcrumb-container {
    height: $base-nav-bar-height;
    font-size: $base-font-size-default;
    line-height: $base-nav-bar-height;

    ::v-deep {
      .el-breadcrumb__item {
        .el-breadcrumb__inner {
          a {
            display: flex;
            float: left;
            font-weight: normal;
            color: #515a6e;

            i {
              margin-right: 3px;
            }
          }
        }

        &:last-child {
          .el-breadcrumb__inner {
            a {
              color: #999;
            }
          }
        }
      }
    }
  }
</style>


================================================
FILE: src/layouts/components/VabLogo/index.vue
================================================
<template>
  <div :class="'logo-container-' + layout">
    <router-link to="/">
      <!-- 这里是logo变更的位置 -->
      <vab-remix-icon v-if="logo" class="logo" :icon-class="logo" />
      <span
        class="title"
        :class="{ 'hidden-xs-only': layout === 'horizontal' }"
        :title="title"
      >
        {{ title }}
      </span>
    </router-link>
  </div>
</template>
<script>
  import { mapGetters } from 'vuex'

  export default {
    name: 'VabLogo',
    data() {
      return {
        title: this.$baseTitle,
      }
    },
    computed: {
      ...mapGetters({
        logo: 'settings/logo',
        layout: 'settings/layout',
      }),
    },
  }
</script>
<style lang="scss" scoped>
  @mixin container {
    position: relative;
    height: $base-top-bar-height;
    overflow: hidden;
    line-height: $base-top-bar-height;
    background: $base-menu-background;
  }

  @mixin logo {
    display: inline-block;
    width: 28px;
    height: 28px;
    margin-right: 3px;
    color: $base-title-color;
    vertical-align: middle;
  }

  @mixin title {
    display: inline-block;
    overflow: hidden;
    font-size: 18px;
    line-height: 55px;
    color: $base-title-color;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
  }

  .logo-container-horizontal {
    @include container;

    .logo {
      @include logo;
    }

    .title {
      @include title;
    }
  }

  .logo-container-vertical {
    @include container;

    height: $base-logo-height;
    line-height: $base-logo-height;
    text-align: center;

    .logo {
      @include logo;
    }

    .title {
      @include title;

      max-width: calc(#{$base-left-menu-width} - 60px);
    }
  }
</style>


================================================
FILE: src/layouts/components/VabNavBar/index.vue
================================================
<template>
  <div class="nav-bar-container">
    <el-row :gutter="15">
      <el-col :xs="4" :sm="12" :md="12" :lg="12" :xl="12">
        <div class="left-panel">
          <i
            :class="collapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
            :title="collapse ? '展开' : '收起'"
            class="fold-unfold"
            @click="handleCollapse"
          ></i>
          <vab-breadcrumb class="hidden-xs-only" />
        </div>
      </el-col>
      <el-col :xs="20" :sm="12" :md="12" :lg="12" :xl="12">
        <div class="right-panel">
          <vab-error-log />
          <vab-full-screen-bar @refresh="refreshRoute" />
          <vab-theme-bar class="hidden-xs-only" />
          <vab-icon
            title="重载所有路由"
            :pulse="pulse"
            :icon="['fas', 'redo']"
            @click="refreshRoute"
          />
          <vab-avatar />
          <!--  <vab-icon
            title="退出系统"
            :icon="['fas', 'sign-out-alt']"
            @click="logout"
          />-->
        </div>
      </el-col>
    </el-row>
  </div>
</template>

<script>
  import { mapActions, mapGetters } from 'vuex'

  export default {
    name: 'VabNavBar',
    data() {
      return {
        pulse: false,
      }
    },
    computed: {
      ...mapGetters({
        collapse: 'settings/collapse',
        visitedRoutes: 'tabsBar/visitedRoutes',
        device: 'settings/device',
        routes: 'routes/routes',
      }),
    },
    methods: {
      ...mapActions({
        changeCollapse: 'settings/changeCollapse',
      }),
      handleCollapse() {
        this.changeCollapse()
      },
      async refreshRoute() {
        this.$baseEventBus.$emit('reload-router-view')
        this.pulse = true
        setTimeout(() => {
          this.pulse = false
        }, 1000)
      },
    },
  }
</script>

<style lang="scss" scoped>
  .nav-bar-container {
    position: relative;
    height: $base-nav-bar-height;
    padding-right: $base-padding;
    padding-left: $base-padding;
    overflow: hidden;
    user-select: none;
    background: $base-color-white;
    box-shadow: $base-box-shadow;

    .left-panel {
      display: flex;
      align-items: center;
      justify-items: center;
      height: $base-nav-bar-height;

      .fold-unfold {
        color: $base-color-gray;
        cursor: pointer;
      }

      ::v-deep {
        .breadcrumb-container {
          margin-left: 10px;
        }
      }
    }

    .right-panel {
      display: flex;
      align-content: center;
      align-items: center;
      justify-content: flex-end;
      height: $base-nav-bar-height;

      ::v-deep {
        svg {
          width: 1em;
          height: 1em;
          margin-right: 15px;
          font-size: $base-font-size-small;
          color: $base-color-gray;
          cursor: pointer;
          fill: $base-color-gray;
        }

        button {
          svg {
            margin-right: 0;
            color: $base-color-white;
            cursor: pointer;
            fill: $base-color-white;
          }
        }

        .el-badge {
          margin-right: 15px;
        }
      }
    }
  }
</style>


================================================
FILE: src/layouts/components/VabThemeBar/index.vue
================================================
<template>
  <span v-if="themeBar">
    <vab-icon
      title="主题配置"
      :icon="['fas', 'palette']"
      @click="handleOpenThemeBar"
    />
    <div class="theme-bar-setting">
      <div @click="handleOpenThemeBar">
        <vab-icon :icon="['fas', 'palette']" />
        <p>主题配置</p>
      </div>
      <div @click="handleGetCode">
        <vab-icon :icon="['fas', 'laptop-code']"></vab-icon>
        <p>拷贝源码</p>
      </div>
    </div>

    <el-drawer
      title="主题配置"
      :visible.sync="drawerVisible"
      direction="rtl"
      append-to-body
      size="470px"
    >
      <el-scrollbar style="height: 94vh; overflow: hidden">
        <div class="el-drawer__body">
          <el-form ref="form" :model="theme" label-position="top">
            <el-form-item label="主题">
              <el-radio-group v-model="theme.name">
                <el-radio-button label="default">默认</el-radio-button>
                <el-radio-button label="green">绿荫草场</el-radio-button>
                <el-radio-button label="glory">荣耀典藏</el-radio-button>
                <!-- <el-radio-button label="orean">海洋之心</el-radio-button>
                <el-radio-button label="red">月上重火</el-radio-button> -->
              </el-radio-group>
            </el-form-item>
            <el-form-item label="布局">
              <el-radio-group v-model="theme.layout">
                <el-radio-button label="vertical">纵向布局</el-radio-button>
                <el-radio-button label="horizontal">横向布局</el-radio-button>
              </el-radio-group>
            </el-form-item>
            <el-form-item label="头部">
              <el-radio-group v-model="theme.header">
                <el-radio-button label="fixed">固定头部</el-radio-button>
                <el-radio-button label="noFixed">不固定头部</el-radio-button>
              </el-radio-group>
            </el-form-item>
            <el-form-item label="多标签">
              <el-radio-group v-model="theme.tabsBar">
                <el-radio-button label="true">开启</el-radio-button>
                <el-radio-button label="false">不开启</el-radio-button>
              </el-radio-group>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="handleSaveTheme">
                保存
              </el-button>
            </el-form-item>
          </el-form>
        </div>
      </el-scrollbar>
    </el-drawer>
  </span>
</template>

<script>
  import variables from '@/styles/variables.scss'
  import { mapActions, mapGetters } from 'vuex'
  import { layout as defaultLayout } from '@/config'
  export default {
    name: 'VabThemeBar',
    data() {
      return {
        drawerVisible: false,
        theme: {
          name: 'default',
          layout: '',
          header: 'fixed',
          tabsBar: '',
        },
      }
    },
    computed: {
      ...mapGetters({
        layout: 'settings/layout',
        header: 'settings/header',
        tabsBar: 'settings/tabsBar',
        themeBar: 'settings/themeBar',
      }),
    },
    created() {
      this.$baseEventBus.$on('theme', () => {
        this.handleOpenThemeBar()
      })
      const theme = localStorage.getItem('vue-admin-better-theme')
      if (null !== theme) {
        this.theme = JSON.parse(theme)
        this.handleSetTheme()
      } else {
        this.theme.layout = this.layout
        this.theme.header = this.header
        this.theme.tabsBar = this.tabsBar
      }
    },
    methods: {
      ...mapActions({
        changeLayout: 'settings/changeLayout',
        changeHeader: 'settings/changeHeader',
        changeTabsBar: 'settings/changeTabsBar',
      }),
      handleIsMobile() {
        return document.body.getBoundingClientRect().width - 1 < 992
      },
      handleOpenThemeBar() {
        this.drawerVisible = true
      },
      handleSetTheme() {
        let { name, layout, header, tabsBar } = this.theme
        localStorage.setItem(
          'vue-admin-better-theme',
          `{
            "name":"${name}",
            "layout":"${layout}",
            "header":"${header}",
            "tabsBar":"${tabsBar}"
          }`
        )
        if (!this.handleIsMobile()) this.changeLayout(layout)
        this.changeHeader(header)
        this.changeTabsBar(tabsBar)
        document.getElementsByTagName(
          'body'
        )[0].className = `vue-admin-better-theme-${name}`
        this.drawerVisible = false
      },
      handleSaveTheme() {
        this.handleSetTheme()
      },
      handleSetDfaultTheme() {
        let { name } = this.theme
        document
          .getElementsByTagName('body')[0]
          .classList.remove(`vue-admin-better-theme-${name}`)
        localStorage.removeItem('vue-admin-better-theme')
        this.$refs['form'].resetFields()
        Object.assign(this.$data, this.$options.data())
        this.changeHeader(defaultLayout)
        this.theme.name = 'default'
        this.theme.layout = this.layout
        this.theme.header = this.header
        this.theme.tabsBar = this.tabsBar
        this.drawerVisible = false
        location.reload()
      },
      handleGetCode() {
        const url =
          'https://github.com/chuzhixin/vue-admin-better/tree/master/src/views'
        let path = this.$route.path + '/index.vue'
        if (path === '/vab/menu1/menu1-1/menu1-1-1/index.vue') {
          path = '/vab/nested/menu1/menu1-1/menu1-1-1/index.vue'
        }
        if (path === '/vab/icon/awesomeIcon/index.vue') {
          path = '/vab/icon/index.vue'
        }
        if (path === '/vab/icon/remixIcon/index.vue') {
          path = '/vab/icon/remixIcon.vue'
        }
        if (path === '/vab/icon/colorfulIcon/index.vue') {
          path = '/vab/icon/colorfulIcon.vue'
        }
        if (path === '/vab/table/comprehensiveTable/index.vue') {
          path = '/vab/table/index.vue'
        }
        if (path === '/vab/table/inlineEditTable/index.vue') {
          path = '/vab/table/inlineEditTable.vue'
        }
        window.open(url + path)
      },
    },
  }
</script>

<style lang="scss" scoped>
  @mixin right-bar {
    position: fixed;
    right: 0;
    z-index: $base-z-index;
    width: 60px;
    min-height: 60px;
    text-align: center;
    cursor: pointer;
    background: $base-color-blue;
    border-radius: $base-border-radius;

    > div {
      padding-top: 10px;
      border-bottom: 0 !important;

      &:hover {
        opacity: 0.9;
      }

      & + div {
        border-top: 1px solid $base-color-white;
      }

      p {
        padding: 0;
        margin: 0;
        font-size: $base-font-size-small;
        line-height: 30px;
        color: $base-color-white;
      }
    }
  }

  .theme-bar-setting {
    @include right-bar;

    top: calc((100vh - 110px) / 2);

    ::v-deep {
      svg:not(:root).svg-inline--fa {
        display: block;
        margin-right: auto;
        margin-left: auto;
        color: $base-color-white;
      }

      .svg-icon {
        display: block;
        margin-right: auto;
        margin-left: auto;
        font-size: 20px;
        color: $base-color-white;
        fill: $base-color-white;
      }
    }
  }

  .el-drawer__body {
    padding: 20px;
  }
</style>
<style lang="scss">
  .el-drawer__wrapper {
    outline: none !important;

    * {
      outline: none !important;
    }
  }

  .vab-color-picker {
    .el-color-dropdown__link-btn {
      display: none;
    }
  }
</style>


================================================
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
================================================
<template>
  <div class="vue-admin-better-wrapper" :class="classObj">
    <div
      v-if="'horizontal' === layout"
      class="layout-container-horizontal"
      :class="{
        fixed: header === 'fixed',
        'no-tabs-bar': tabsBar === 'false' || tabsBar === false,
      }"
    >
      <div :class="header === 'fixed' ? 'fixed-header' : ''">
        <vab-top-bar />
        <div
          v-if="tabsBar === 'true' || tabsBar === true"
          :class="{ 'tag-view-show': tabsBar }"
        >
          <div class="vab-main">
            <vab-tabs-bar />
          </div>
        </div>
      </div>
      <div class="vab-main main-padding">
        <vab-ad />
        <vab-app-main />
      </div>
    </div>
    <div
      v-else
      class="layout-container-vertical"
      :class="{
        fixed: header === 'fixed',
        'no-tabs-bar': tabsBar === 'false' || tabsBar === false,
      }"
    >
      <div
        v-if="device === 'mobile' && collapse === false"
        class="mask"
        @click="handleFoldSideBar"
      />
      <vab-side-bar />
      <div class="vab-main" :class="collapse ? 'is-collapse-main' : ''">
        <div :class="header === 'fixed' ? 'fixed-header' : ''">
          <vab-nav-bar />
          <vab-tabs-bar v-if="tabsBar === 'true' || tabsBar === true" />
        </div>
        <vab-ad />
        <vab-app-main />
      </div>
    </div>
    <el-backtop />
  </div>
</template>

<script>
  import { mapActions, mapGetters } from 'vuex'
  import { tokenName } from '@/config'
  export default {
    name: 'Layout',
    data() {
      return { oldLayout: '' }
    },
    computed: {
      ...mapGetters({
        layout: 'settings/layout',
        tabsBar: 'settings/tabsBar',
        collapse: 'settings/collapse',
        header: 'settings/header',
        device: 'settings/device',
      }),
      classObj() {
        return {
          mobile: this.device === 'mobile',
        }
      },
    },
    beforeMount() {
      window.addEventListener('resize', this.handleResize)
    },
    beforeDestroy() {
      window.removeEventListener('resize', this.handleResize)
    },
    mounted() {
      this.oldLayout = this.layout
      const userAgent = navigator.userAgent
      if (userAgent.includes('Juejin')) {
        this.$baseAlert(
          'vue-admin-better不支持在掘金内置浏览器演示,请手动复制以下地址到浏览器中查看http://mpfhrd48.sanxing.uz7.cn/vue-admin-better'
        )
      }
      const isMobile = this.handleIsMobile()
      if (isMobile) {
        if (isMobile) {
          //横向布局时如果是手机端访问那么改成纵向版
          this.$store.dispatch('settings/changeLayout', 'vertical')
        } else {
          this.$store.dispatch('settings/changeLayout', this.oldLayout)
        }
        this.$store.dispatch('settings/toggleDevice', 'mobile')
        setTimeout(() => {
          this.$store.dispatch('settings/foldSideBar')
        }, 2000)
      } else {
        this.$store.dispatch('settings/openSideBar')
      }
      this.$nextTick(() => {
        window.addEventListener(
          'storage',
          (e) => {
            if (e.key === tokenName || e.key === null) window.location.reload()
            if (e.key === tokenName && e.value === null)
              window.location.reload()
          },
          false
        )
      })
    },
    methods: {
      ...mapActions({
        handleFoldSideBar: 'settings/foldSideBar',
      }),
      handleIsMobile() {
        return document.body.getBoundingClientRect().width - 1 < 992
      },
      handleResize() {
        if (!document.hidden) {
          const isMobile = this.handleIsMobile()
          if (isMobile) {
            //横向布局时如果是手机端访问那么改成纵向版
            this.$store.dispatch('settings/changeLayout', 'vertical')
          } else {
            this.$store.dispatch('settings/changeLayout', this.oldLayout)
          }

          this.$store.dispatch(
            'settings/toggleDevice',
            isMobile ? 'mobile' : 'desktop'
          )
        }
      },
    },
  }
</script>

<style lang="scss" scoped>
  @mixin fix-header {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    z-index: $base-z-index - 2;
    width: 100%;
    overflow: hidden;
  }

  .vue-admin-better-wrapper {
    position: relative;
    width: 100%;
    height: 100%;

    .layout-container-horizontal {
      position: relative;

      &.fixed {
        padding-top: calc(#{$base-top-bar-height} + #{$base-tabs-bar-height});
      }

      &.fixed.no-tabs-bar {
        padding-top: $base-top-bar-height;
      }

      ::v-deep {
        .vab-main {
          width: 88%;
          margin: auto;
        }

        .fixed-header {
          @include fix-header;
        }

        .tag-view-show {
          background: $base-color-white;
          box-shadow: $base-box-shadow;
        }

        .nav-bar-container {
          .fold-unfold {
            display: none;
          }
        }

        .main-padding {
          .app-main-container {
            margin-top: $base-padding;
            margin-bottom: $base-padding;
            background: $base-color-white;
          }
        }
      }
    }

    .layout-container-vertical {
      position: relative;

      .mask {
        position: fixed;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: $base-z-index - 1;
        width: 100%;
        height: 100vh;
        overflow: hidden;
        background: #000;
        opacity: 0.5;
      }

      &.fixed {
        padding-top: calc(#{$base-nav-bar-height} + #{$base-tabs-bar-height});
      }

      &.fixed.no-tabs-bar {
        padding-top: $base-nav-bar-height;
      }

      .vab-main {
        position: relative;
        min-height: 100%;
        margin-left: $base-left-menu-width;
        background: #f6f8f9;
        transition: $base-transition;

        ::v-deep {
          .fixed-header {
            @include fix-header;

            left: $base-left-menu-width;
            width: $base-right-content-width;
            box-shadow: $base-box-shadow;
            transition: $base-transition;
          }

          .nav-bar-container {
            position: relative;
            box-sizing: border-box;
          }

          .tabs-bar-container {
            box-sizing: border-box;
          }

          .app-main-container {
            width: calc(100% - #{$base-padding} - #{$base-padding});
            margin: $base-padding auto;
            background: $base-color-white;
            border-radius: $base-border-radius;
          }
        }

        &.is-collapse-main {
          margin-left: $base-left-menu-width-min;

          ::v-deep {
            .fixed-header {
              left: $base-left-menu-width-min;
              width: calc(100% - 65px);
            }
          }
        }
      }
    }

    /* 手机端开始 */
    &.mobile {
      ::v-deep {
        .el-pager,
        .el-pagination__jump {
          display: none;
        }

        .layout-container-vertical {
          .el-scrollbar.side-bar-container.is-collapse {
            width: 0;
          }

          .vab-main {
            width: 100%;
            margin-left: 0;
          }
        }

        .vab-main {
          .fixed-header {
            left: 0 !important;
            width: 100% !important;
          }
        }
      }
    }

    /* 手机端结束 */
  }
</style>


================================================
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提供任何更新维护,请<a target="_blank" style="color:blue" href="https://www.microsoft.com/zh-cn/edge/">点击此处</a>访问微软官网更新浏览器,如果您使用的是双核浏览器,请您切换浏览器内核为极速模式',
    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 
Download .txt
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
Download .txt
SYMBOL INDEX (108 symbols across 27 files)

FILE: layouts/Permissions/permissions.js
  method inserted (line 4) | inserted(element, binding) {

FILE: layouts/index.js
  method donationConsole (line 5) | donationConsole() {

FILE: mock/controller/ad.js
  method response (line 15) | response() {

FILE: mock/controller/router.js
  method response (line 45) | response() {

FILE: mock/controller/user.js
  method response (line 11) | response() {
  method response (line 26) | response(config) {
  method response (line 45) | response() {
  method response (line 55) | response(config) {
  method response (line 88) | response() {

FILE: mock/mockServer.js
  method response (line 42) | response(req, res) {

FILE: mock/utils/index.js
  function handleRandomImage (line 12) | function handleRandomImage(width = 50, height = 50) {
  function handleMockArray (line 21) | function handleMockArray() {

FILE: src/api/ad.js
  function getList (line 3) | function getList(data) {

FILE: src/api/publicKey.js
  function getPublicKey (line 3) | function getPublicKey() {

FILE: src/api/router.js
  function getRouterList (line 3) | function getRouterList(data) {

FILE: src/api/user.js
  function login (line 5) | async function login(data) {
  function getUserInfo (line 16) | function getUserInfo(accessToken) {
  function logout (line 26) | function logout() {
  function register (line 33) | function register() {

FILE: src/router/index.js
  function resetRouter (line 82) | function resetRouter() {

FILE: src/store/modules/errorLog.js
  method addErrorLog (line 11) | addErrorLog(state, errorLog) {
  method addErrorLog (line 19) | addErrorLog({ commit }, errorLog) {
  method clearErrorLog (line 22) | clearErrorLog({ commit }) {

FILE: src/store/modules/routes.js
  method setRoutes (line 15) | setRoutes(state, routes) {
  method setAllRoutes (line 18) | setAllRoutes(state, routes) {
  method setPartialRoutes (line 21) | setPartialRoutes(state, routes) {
  method setRoutes (line 26) | async setRoutes({ commit }, permissions) {
  method setAllRoutes (line 35) | async setAllRoutes({ commit }) {
  method setPartialRoutes (line 42) | setPartialRoutes({ commit }, accessRoutes) {

FILE: src/store/modules/settings.js
  method changeLayout (line 52) | changeLayout({ commit }, layout) {
  method changeHeader (line 55) | changeHeader({ commit }, header) {
  method changeTabsBar (line 58) | changeTabsBar({ commit }, tabsBar) {
  method changeCollapse (line 61) | changeCollapse({ commit }) {
  method foldSideBar (line 64) | foldSideBar({ commit }) {
  method openSideBar (line 67) | openSideBar({ commit }) {
  method toggleDevice (line 70) | toggleDevice({ commit }, device) {

FILE: src/store/modules/table.js
  method setTableCode (line 12) | setTableCode(state, srcCode) {
  method setTableCode (line 17) | setTableCode({ commit }, srcCode) {

FILE: src/store/modules/tabsBar.js
  method addVisitedRoute (line 13) | addVisitedRoute(state, route) {
  method delVisitedRoute (line 21) | delVisitedRoute(state, route) {
  method delOthersVisitedRoute (line 26) | delOthersVisitedRoute(state, route) {
  method delLeftVisitedRoute (line 31) | delLeftVisitedRoute(state, route) {
  method delRightVisitedRoute (line 38) | delRightVisitedRoute(state, route) {
  method delAllVisitedRoutes (line 45) | delAllVisitedRoutes(state) {
  method updateVisitedRoute (line 48) | updateVisitedRoute(state, route) {
  method addVisitedRoute (line 55) | addVisitedRoute({ commit }, route) {
  method delRoute (line 58) | async delRoute({ dispatch, state }, route) {
  method delVisitedRoute (line 64) | delVisitedRoute({ commit, state }, route) {
  method delOthersRoutes (line 68) | async delOthersRoutes({ dispatch, state }, route) {
  method delLeftRoutes (line 74) | async delLeftRoutes({ dispatch, state }, route) {
  method delRightRoutes (line 80) | async delRightRoutes({ dispatch, state }, route) {
  method delOthersVisitedRoute (line 86) | delOthersVisitedRoute({ commit, state }, route) {
  method delLeftVisitedRoute (line 90) | delLeftVisitedRoute({ commit, state }, route) {
  method delRightVisitedRoute (line 94) | delRightVisitedRoute({ commit, state }, route) {
  method delAllRoutes (line 98) | async delAllRoutes({ dispatch, state }, route) {
  method delAllVisitedRoutes (line 104) | delAllVisitedRoutes({ commit, state }) {
  method updateVisitedRoute (line 108) | updateVisitedRoute({ commit }, route) {

FILE: src/store/modules/user.js
  method setAccessToken (line 29) | setAccessToken(state, accessToken) {
  method setUsername (line 33) | setUsername(state, username) {
  method setAvatar (line 36) | setAvatar(state, avatar) {
  method setPermissions (line 39) | setPermissions(state, permissions) {
  method setPermissions (line 44) | setPermissions({ commit }, permissions) {
  method login (line 47) | async login({ commit }, userInfo) {
  method getUserInfo (line 71) | async getUserInfo({ commit, state }) {
  method logout (line 88) | async logout({ dispatch }) {
  method resetAccessToken (line 93) | resetAccessToken({ commit }) {

FILE: src/utils/accessToken.js
  function getAccessToken (line 8) | function getAccessToken() {
  function setAccessToken (line 28) | function setAccessToken(accessToken) {
  function removeAccessToken (line 47) | function removeAccessToken() {

FILE: src/utils/encrypt.js
  function encryptedData (line 13) | async function encryptedData(data) {
  function decryptedData (line 39) | function decryptedData(data) {

FILE: src/utils/handleRoutes.js
  function convertRouter (line 7) | function convertRouter(asyncRoutes) {
  function hasPermission (line 36) | function hasPermission(permissions, route) {
  function filterAsyncRoutes (line 51) | function filterAsyncRoutes(routes, permissions) {

FILE: src/utils/index.js
  function parseTime (line 8) | function parseTime(time, cFormat) {
  function formatTime (line 54) | function formatTime(time, option) {
  function paramObj (line 98) | function paramObj(url) {
  function translateDataToTree (line 120) | function translateDataToTree(data) {
  function translateTreeToData (line 151) | function translateTreeToData(data) {
  function tenBitTimestamp (line 178) | function tenBitTimestamp(time) {
  function thirteenBitTimestamp (line 200) | function thirteenBitTimestamp(time) {
  function uuid (line 222) | function uuid(length = 32) {
  function random (line 238) | function random(m, n) {

FILE: src/utils/pageTitle.js
  function getPageTitle (line 9) | function getPageTitle(pageTitle) {

FILE: src/utils/permission.js
  function checkPermission (line 9) | function checkPermission(value) {

FILE: src/utils/static.js
  function mockXHR (line 15) | function mockXHR() {

FILE: src/utils/validate.js
  function isExternal (line 7) | function isExternal(path) {
  function isPassword (line 17) | function isPassword(str) {
  function isNumber (line 27) | function isNumber(value) {
  function isName (line 38) | function isName(value) {
  function isIP (line 49) | function isIP(ip) {
  function isUrl (line 61) | function isUrl(url) {
  function isLowerCase (line 73) | function isLowerCase(str) {
  function isUpperCase (line 84) | function isUpperCase(str) {
  function isAlphabets (line 95) | function isAlphabets(str) {
  function isString (line 106) | function isString(str) {
  function isArray (line 116) | function isArray(arg) {
  function isPort (line 129) | function isPort(str) {
  function isPhone (line 141) | function isPhone(str) {
  function isIdCard (line 152) | function isIdCard(str) {
  function isEmail (line 164) | function isEmail(str) {
  function isChina (line 175) | function isChina(str) {
  function isBlank (line 186) | function isBlank(str) {
  function isTel (line 202) | function isTel(str) {
  function isNum (line 214) | function isNum(str) {

FILE: vue.config.js
  method configureWebpack (line 60) | configureWebpack() {
  method chainWebpack (line 75) | chainWebpack(config) {
  method additionalData (line 177) | additionalData(content, loaderContext) {
Condensed preview — 114 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (317K chars).
[
  {
    "path": ".browserslistrc",
    "chars": 31,
    "preview": "> 1%\nlast 2 versions\nnot dead\n\n"
  },
  {
    "path": ".editorconfig",
    "chars": 188,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".eslintignore",
    "chars": 46,
    "preview": "src/assets\nsrc/icons\npublic\ndist\nnode_modules\n"
  },
  {
    "path": ".eslintrc.js",
    "chars": 548,
    "preview": "module.exports = {\n  root: true,\n  env: {\n    node: true,\n  },\n  extends: ['plugin:vue/recommended', '@vue/prettier'],\n "
  },
  {
    "path": ".gitattributes",
    "chars": 180,
    "preview": "*.html text eol=lf\n*.css text eol=lf\n*.js text eol=lf\n*.scss text eol=lf\n*.vue text eol=lf\n*.hbs text eol=lf\n*.sh text e"
  },
  {
    "path": ".gitignore",
    "chars": 303,
    "preview": ".DS_Store\nnode_modules\ndist\n.env.local\n.env.*.local\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.idea\n*.suo\n*.ntvs*\n*"
  },
  {
    "path": ".stylelintrc.js",
    "chars": 96,
    "preview": "module.exports = {\n  extends: ['stylelint-config-recess-order', 'stylelint-config-prettier'],\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 1590,
    "preview": "{\n  \"[vue]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"editor.quickSuggestions\": {\n    \"strings\""
  },
  {
    "path": "LICENSE",
    "chars": 16725,
    "preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
  },
  {
    "path": "README.en.md",
    "chars": 10071,
    "preview": "[简体中文](./README.md) | English\n\n<div align=\"center\"><img width=\"200\" src=\"https://fastly.jsdelivr.net/gh/chuzhixin/image/"
  },
  {
    "path": "README.md",
    "chars": 7167,
    "preview": "<div style=\"filter: grayscale(100%)\">\n\n简体中文 | [English](./README.en.md)\n\n<div align=\"center\"><img width=\"200\" src=\"https"
  },
  {
    "path": "babel.config.js",
    "chars": 66,
    "preview": "module.exports = {\n  presets: ['@vue/cli-plugin-babel/preset'],\n}\n"
  },
  {
    "path": "deploy.sh",
    "chars": 329,
    "preview": "#!/usr/bin/env bash\nset -e\nnpm run build\ncd dist\ntouch .nojekyll\ngit init\ngit add -A\ngit commit -m 'deploy'\ngit push -f "
  },
  {
    "path": "http/mock.http",
    "chars": 1800,
    "preview": "\n###/changeLog/getList###mockServer\nPOST http://localhost:80/mock-server/changeLog/getList\nContent-Type: application/x-w"
  },
  {
    "path": "layouts/Permissions/index.js",
    "chars": 256,
    "preview": "import permissions from './permissions'\n\nconst install = function (Vue) {\n  Vue.directive('permissions', permissions)\n}\n"
  },
  {
    "path": "layouts/Permissions/permissions.js",
    "chars": 414,
    "preview": "import store from '@/store'\n\nexport default {\n  inserted(element, binding) {\n    const { value } = binding\n    const per"
  },
  {
    "path": "layouts/VabColorfullIcon/index.vue",
    "chars": 1205,
    "preview": "<template>\n  <img\n    v-if=\"isExternal\"\n    :src=\"styleExternalIcon\"\n    class=\"svg-external-icon svg-icon\"\n    v-on=\"$l"
  },
  {
    "path": "layouts/VabErrorLog/index.vue",
    "chars": 3414,
    "preview": "<template>\n  <div v-if=\"errorLogs.length > 0\">\n    <el-badge\n      :value=\"errorLogs.length\"\n      @click.native=\"dialog"
  },
  {
    "path": "layouts/VabFullScreenBar/index.vue",
    "chars": 1054,
    "preview": "<template>\n  <span :title=\"isFullscreen ? '退出全屏' : '进入全屏'\">\n    <vab-icon\n      :icon=\"[\n        'fas',\n        isFullsc"
  },
  {
    "path": "layouts/VabGithubCorner/index.vue",
    "chars": 2008,
    "preview": "<template>\n  <a\n    href=\"https://github.com/chuzhixin/vue-admin-better\"\n    target=\"_blank\"\n    class=\"github-corner\"\n "
  },
  {
    "path": "layouts/VabQueryForm/VabQueryFormBottomPanel.vue",
    "chars": 305,
    "preview": "<template>\n  <el-col :span=\"24\">\n    <div class=\"bottom-panel\">\n      <slot></slot>\n    </div>\n  </el-col>\n</template>\n\n"
  },
  {
    "path": "layouts/VabQueryForm/VabQueryFormLeftPanel.vue",
    "chars": 408,
    "preview": "<template>\n  <el-col :xs=\"24\" :sm=\"24\" :md=\"24\" :lg=\"span\" :xl=\"span\">\n    <div class=\"left-panel\">\n      <slot></slot>\n"
  },
  {
    "path": "layouts/VabQueryForm/VabQueryFormRightPanel.vue",
    "chars": 410,
    "preview": "<template>\n  <el-col :xs=\"24\" :sm=\"24\" :md=\"24\" :lg=\"span\" :xl=\"span\">\n    <div class=\"right-panel\">\n      <slot></slot>"
  },
  {
    "path": "layouts/VabQueryForm/VabQueryFormTopPanel.vue",
    "chars": 297,
    "preview": "<template>\n  <el-col :span=\"24\">\n    <div class=\"top-panel\">\n      <slot></slot>\n    </div>\n  </el-col>\n</template>\n\n<sc"
  },
  {
    "path": "layouts/VabQueryForm/index.vue",
    "chars": 954,
    "preview": "<template>\n  <el-row :gutter=\"0\" class=\"vab-query-form\">\n    <slot></slot>\n  </el-row>\n</template>\n\n<script>\n  export de"
  },
  {
    "path": "layouts/VabRemixIcon/index.vue",
    "chars": 1376,
    "preview": "<template>\n  <div\n    v-if=\"isExternal\"\n    :style=\"styleExternalIcon\"\n    class=\"svg-external-icon svg-icon\"\n    v-on=\""
  },
  {
    "path": "layouts/VabSideBar/components/VabMenuItem.vue",
    "chars": 2186,
    "preview": "<template>\n  <el-menu-item :index=\"handlePath(routeChildren.path)\" @click=\"handleLink\">\n    <vab-icon\n      v-if=\"routeC"
  },
  {
    "path": "layouts/VabSideBar/components/VabSideBarItem.vue",
    "chars": 2350,
    "preview": "<template>\n  <component\n    :is=\"menuComponent\"\n    v-if=\"!item.hidden\"\n    :item=\"item\"\n    :full-path=\"fullPath\"\n    :"
  },
  {
    "path": "layouts/VabSideBar/components/VabSubmenu.vue",
    "chars": 1240,
    "preview": "<template>\n  <el-submenu\n    ref=\"subMenu\"\n    :index=\"handlePath(item.path)\"\n    :popper-append-to-body=\"false\"\n  >\n   "
  },
  {
    "path": "layouts/VabSideBar/index.vue",
    "chars": 3189,
    "preview": "<template>\n  <el-scrollbar class=\"side-bar-container\" :class=\"{ 'is-collapse': collapse }\">\n    <vab-logo />\n    <el-men"
  },
  {
    "path": "layouts/VabTabsBar/index.vue",
    "chars": 7922,
    "preview": "<template>\n  <div id=\"tabs-bar-container\" class=\"tabs-bar-container\">\n    <el-tabs\n      v-model=\"tabActive\"\n      type="
  },
  {
    "path": "layouts/VabTopBar/index.vue",
    "chars": 5617,
    "preview": "<template>\n  <div class=\"top-bar-container\">\n    <div class=\"vab-main\">\n      <el-row>\n        <el-col :xl=\"7\" :lg=\"7\" :"
  },
  {
    "path": "layouts/index.js",
    "chars": 1020,
    "preview": "module.exports = {\n  webpackBarName: 'vue-admin-better',\n  webpackBanner:\n    ' build: vue-admin-better \\n vue-admin-bet"
  },
  {
    "path": "layouts/package.json",
    "chars": 68,
    "preview": "{\n  \"name\": \"layouts\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\"\n}\n"
  },
  {
    "path": "layouts/prettier.config.js",
    "chars": 353,
    "preview": "module.exports = {\n  printWidth: 80,\n  tabWidth: 2,\n  useTabs: false,\n  semi: false,\n  singleQuote: true,\n  quoteProps: "
  },
  {
    "path": "license.md",
    "chars": 16725,
    "preview": "Mozilla Public License Version 2.0\n==================================\n\n1. Definitions\n--------------\n\n1.1. \"Contributor\""
  },
  {
    "path": "mock/controller/ad.js",
    "chars": 463,
    "preview": "const data = [\n  {\n    title: 'vue-admin-better-pro 1.7版本已发布,点我提前体验',\n    url: 'https://chu1204505056.gitee.io/vue-admin"
  },
  {
    "path": "mock/controller/router.js",
    "chars": 898,
    "preview": "const data = [\n  {\n    path: '/',\n    component: 'Layout',\n    redirect: 'index',\n    children: [\n      {\n        path: "
  },
  {
    "path": "mock/controller/user.js",
    "chars": 2085,
    "preview": "const accessTokens = {\n  admin: 'admin-accessToken',\n  editor: 'editor-accessToken',\n  test: 'test-accessToken',\n}\n\nmodu"
  },
  {
    "path": "mock/index.js",
    "chars": 349,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com\n * @description 导入所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mo"
  },
  {
    "path": "mock/mockServer.js",
    "chars": 2505,
    "preview": "const chokidar = require('chokidar')\nconst bodyParser = require('body-parser')\nconst chalk = require('chalk')\nconst path"
  },
  {
    "path": "mock/utils/index.js",
    "chars": 1189,
    "preview": "const { Random } = require('mockjs')\nconst { join } = require('path')\nconst fs = require('fs')\n\n/**\n * @author chuzhixin"
  },
  {
    "path": "package.json",
    "chars": 2602,
    "preview": "{\n  \"name\": \"vue-admin-better-template\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"author\": \"vue-admin-better\",\n  \"par"
  },
  {
    "path": "plopfile.js",
    "chars": 561,
    "preview": "const viewGenerator = require('zx-templates/view/prompt')\nconst curdGenerator = require('zx-templates/curd/prompt')\ncons"
  },
  {
    "path": "prettier.config.js",
    "chars": 438,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 代码规范\n */\n\nmodule.exports = {\n  printWidth: 80"
  },
  {
    "path": "public/index.html",
    "chars": 1760,
    "preview": "<!DOCTYPE html>\n<html lang=\"zh-cmn-Hans\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"X-UA-Compatible\" c"
  },
  {
    "path": "public/static/css/loading.css",
    "chars": 1517,
    "preview": ".first-loading-wrp {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  heig"
  },
  {
    "path": "push.sh",
    "chars": 248,
    "preview": "#!/usr/bin/env bash\nset -e\ngit init\ngit add -A\ngit commit -m 'deploy'\ngit push -f \"https://${access_token}@github.com/ch"
  },
  {
    "path": "src/App.vue",
    "chars": 160,
    "preview": "<template>\n  <div id=\"vue-admin-better\">\n    <router-view />\n  </div>\n</template>\n\n<script>\n  export default {\n    name:"
  },
  {
    "path": "src/api/ad.js",
    "chars": 231,
    "preview": "import request from '@/utils/request'\n\nexport function getList(data) {\n  return request({\n    //url: '/ad/getList',\n    "
  },
  {
    "path": "src/api/publicKey.js",
    "chars": 141,
    "preview": "import request from '@/utils/request'\n\nexport function getPublicKey() {\n  return request({\n    url: '/publicKey',\n    me"
  },
  {
    "path": "src/api/router.js",
    "chars": 160,
    "preview": "import request from '@/utils/request'\n\nexport function getRouterList(data) {\n  return request({\n    url: '/menu/navigate"
  },
  {
    "path": "src/api/user.js",
    "chars": 659,
    "preview": "import request from '@/utils/request'\nimport { encryptedData } from '@/utils/encrypt'\nimport { loginRSA, tokenName } fro"
  },
  {
    "path": "src/colorfulIcon/index.js",
    "chars": 404,
    "preview": "const req = require.context('./svg', false, /\\.svg$/),\n  requireAll = (requireContext) => {\n    /*let a = requireContext"
  },
  {
    "path": "src/config/index.js",
    "chars": 231,
    "preview": "/**\n * @description 3个子配置,通用配置|主题配置|网络配置导出\n */\nconst setting = require('./setting.config')\nconst theme = require('./them"
  },
  {
    "path": "src/config/net.config.js",
    "chars": 425,
    "preview": "/**\n * @description 导出默认网路配置\n **/\nconst network = {\n  //配后端数据的接收方式application/json;charset=UTF-8或者application/x-www-form"
  },
  {
    "path": "src/config/permission.js",
    "chars": 2230,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 路由守卫,目前两种模式:all模式与intelligence模式\n */\nimport r"
  },
  {
    "path": "src/config/setting.config.js",
    "chars": 2081,
    "preview": "/**\n * @description 导出默认通用配置\n */\nconst setting = {\n  // 开发以及部署时的URL\n  publicPath: '',\n  // 生产环境构建文件的目录名\n  outputDir: 'di"
  },
  {
    "path": "src/config/settings.js",
    "chars": 162,
    "preview": "/**\n * @description 3个子配置,通用配置|主题配置|网络配置\n */\n//默认配置\nconst { setting, theme, network } = require('./')\nmodule.exports = O"
  },
  {
    "path": "src/config/theme.config.js",
    "chars": 237,
    "preview": "/**\n * @description 导出默认主题配置\n */\nconst theme = {\n  //是否国定头部 固定fixed 不固定noFixed\n  header: 'fixed',\n  //横纵布局 horizontal ve"
  },
  {
    "path": "src/layouts/EmptyLayout.vue",
    "chars": 41,
    "preview": "<template>\n  <router-view />\n</template>\n"
  },
  {
    "path": "src/layouts/components/VabAd/index.vue",
    "chars": 1058,
    "preview": "<template>\n  <div class=\"vab-ad\">\n    <el-carousel\n      v-if=\"adList\"\n      height=\"30px\"\n      direction=\"vertical\"\n  "
  },
  {
    "path": "src/layouts/components/VabAppMain/index.vue",
    "chars": 2317,
    "preview": "<template>\n  <div v-if=\"routerView\" class=\"app-main-container\">\n    <vab-github-corner />\n    <transition mode=\"out-in\" "
  },
  {
    "path": "src/layouts/components/VabAvatar/index.vue",
    "chars": 3132,
    "preview": "<template>\n  <el-dropdown @command=\"handleCommand\">\n    <span class=\"avatar-dropdown\">\n      <!--<el-avatar class=\"user-"
  },
  {
    "path": "src/layouts/components/VabBreadcrumb/index.vue",
    "chars": 1234,
    "preview": "<template>\n  <el-breadcrumb class=\"breadcrumb-container\" separator=\">\">\n    <el-breadcrumb-item v-for=\"item in list\" :ke"
  },
  {
    "path": "src/layouts/components/VabLogo/index.vue",
    "chars": 1716,
    "preview": "<template>\n  <div :class=\"'logo-container-' + layout\">\n    <router-link to=\"/\">\n      <!-- 这里是logo变更的位置 -->\n      <vab-r"
  },
  {
    "path": "src/layouts/components/VabNavBar/index.vue",
    "chars": 3139,
    "preview": "<template>\n  <div class=\"nav-bar-container\">\n    <el-row :gutter=\"15\">\n      <el-col :xs=\"4\" :sm=\"12\" :md=\"12\" :lg=\"12\" "
  },
  {
    "path": "src/layouts/components/VabThemeBar/index.vue",
    "chars": 7404,
    "preview": "<template>\n  <span v-if=\"themeBar\">\n    <vab-icon\n      title=\"主题配置\"\n      :icon=\"['fas', 'palette']\"\n      @click=\"hand"
  },
  {
    "path": "src/layouts/export.js",
    "chars": 898,
    "preview": "/**\n * @author https://vue-admin-better.com (不想保留author可删除)\n * @description 公共布局及样式自动引入\n */\n\nimport Vue from 'vue'\n\ncons"
  },
  {
    "path": "src/layouts/index.vue",
    "chars": 7321,
    "preview": "<template>\n  <div class=\"vue-admin-better-wrapper\" :class=\"classObj\">\n    <div\n      v-if=\"'horizontal' === layout\"\n    "
  },
  {
    "path": "src/main.js",
    "chars": 486,
    "preview": "import Vue from 'vue'\nimport App from './App'\nimport store from './store'\nimport router from './router'\nimport './plugin"
  },
  {
    "path": "src/plugins/element.js",
    "chars": 189,
    "preview": "import Vue from 'vue'\nimport ElementUI from 'element-ui'\nimport 'element-ui/lib/theme-chalk/display.css'\n\nimport '@/styl"
  },
  {
    "path": "src/plugins/index.js",
    "chars": 350,
    "preview": "/* 公共引入,勿随意修改,修改时需经过确认 */\nimport Vue from 'vue'\nimport './element'\nimport './support'\nimport '@/styles/vab.scss'\nimport "
  },
  {
    "path": "src/plugins/support.js",
    "chars": 718,
    "preview": "import { MessageBox } from 'element-ui'\nimport { donation } from '@/config'\nimport { dependencies, repository } from '.."
  },
  {
    "path": "src/plugins/vabIcon.js",
    "chars": 88,
    "preview": "import Vue from 'vue'\nimport VabIcon from 'vab-icon'\n\nVue.component('VabIcon', VabIcon)\n"
  },
  {
    "path": "src/remixIcon/index.js",
    "chars": 404,
    "preview": "const req = require.context('./svg', false, /\\.svg$/),\n  requireAll = (requireContext) => {\n    /*let a = requireContext"
  },
  {
    "path": "src/router/index.js",
    "chars": 2010,
    "preview": "/**\n * @copyright chuzhixin 1204505056@qq.com\n * @description router全局配置,如有必要可分文件抽离\n */\n\nimport Vue from 'vue'\nimport Vu"
  },
  {
    "path": "src/store/index.js",
    "chars": 516,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 导入所有 vuex 模块,自动加入namespaced:true,用于解决vuex命名冲突"
  },
  {
    "path": "src/store/modules/errorLog.js",
    "chars": 564,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 异常捕获的状态拦截,请勿修改\n */\n\nconst state = { errorLogs"
  },
  {
    "path": "src/store/modules/routes.js",
    "chars": 1469,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 路由拦截状态管理,目前两种模式:all模式与intelligence模式,其中partia"
  },
  {
    "path": "src/store/modules/settings.js",
    "chars": 1838,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 所有全局配置的状态管理,如无必要请勿修改\n */\n\nimport defaultSetti"
  },
  {
    "path": "src/store/modules/table.js",
    "chars": 424,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 代码生成机状态管理\n */\n\nconst state = { srcCode: '' }\n"
  },
  {
    "path": "src/store/modules/tabsBar.js",
    "chars": 3553,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description tabsBar多标签页逻辑,前期借鉴了很多开源项目发现都有个共同的特点很繁琐并不符合框架设"
  },
  {
    "path": "src/store/modules/user.js",
    "chars": 2592,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 登录、获取用户信息、退出登录、清除accessToken逻辑,不建议修改\n */\n\nimp"
  },
  {
    "path": "src/styles/element-variables.scss",
    "chars": 31213,
    "preview": "@charset \"utf-8\";\n\n/* Transition\n-------------------------- */\n$--all-transition: all 0.3s cubic-bezier(0.645, 0.045, 0."
  },
  {
    "path": "src/styles/loading.scss",
    "chars": 6168,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 全局加载动画\n */\n\n@charset \"utf-8\";\n\n@import \"./spi"
  },
  {
    "path": "src/styles/normalize.scss",
    "chars": 6139,
    "preview": "@charset \"utf-8\";\n\n/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n   ========"
  },
  {
    "path": "src/styles/spinner/dots.css",
    "chars": 2279,
    "preview": ".dots-loader:not(:required) {\n  position: relative;\n  display: inline-block;\n  width: 7px;\n  height: 7px;\n  margin-botto"
  },
  {
    "path": "src/styles/spinner/gauge.css",
    "chars": 1475,
    "preview": ".gauge-loader:not(:required) {\n  position: relative;\n  display: inline-block;\n  width: 64px;\n  height: 32px;\n  margin-bo"
  },
  {
    "path": "src/styles/spinner/inner-circles.css",
    "chars": 998,
    "preview": ".inner-circles-loader:not(:required) {\n  position: relative;\n  display: inline-block;\n  width: 50px;\n  height: 50px;\n  m"
  },
  {
    "path": "src/styles/spinner/plus.css",
    "chars": 9303,
    "preview": ".plus-loader:not(:required) {\n  position: relative;\n  display: inline-block;\n  width: 48px;\n  height: 48px;\n  margin-bot"
  },
  {
    "path": "src/styles/themes/default.scss",
    "chars": 83,
    "preview": "/* 绿荫草场主题、荣耀典藏主题、暗黑之子主题加QQ讨论群972435319、1139183756后私聊群主获取,获取后将主题放到themes文件夹根目录即可 */\n"
  },
  {
    "path": "src/styles/transition.scss",
    "chars": 287,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description vue过渡动画\n */\n\n@charset \"utf-8\";\n\n.fade-transfo"
  },
  {
    "path": "src/styles/vab.scss",
    "chars": 5010,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 全局样式\n */\n\n@charset \"utf-8\";\n\n@import './norma"
  },
  {
    "path": "src/styles/variables.scss",
    "chars": 2157,
    "preview": "/**\n * @author https://vue-admin-better.com (不想保留author可删除)\n * @description 全局主题变量配置\n */\n/* stylelint-disable */\n@charse"
  },
  {
    "path": "src/utils/accessToken.js",
    "chars": 1731,
    "preview": "import { storage, tokenTableName } from '@/config'\n\n/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @descr"
  },
  {
    "path": "src/utils/encrypt.js",
    "chars": 1944,
    "preview": "import { JSEncrypt } from 'jsencrypt'\nimport { getPublicKey } from '@/api/publicKey'\n\nconst privateKey =\n  'MIICdwIBADAN"
  },
  {
    "path": "src/utils/errorLog.js",
    "chars": 659,
    "preview": "import Vue from 'vue'\nimport store from '@/store'\nimport { isArray, isString } from '@/utils/validate'\nimport { errorLog"
  },
  {
    "path": "src/utils/handleRoutes.js",
    "chars": 1849,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description all模式渲染后端返回路由\n * @param constantRoutes\n * @re"
  },
  {
    "path": "src/utils/index.js",
    "chars": 6367,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 格式化时间\n * @param time\n * @param cFormat\n * @re"
  },
  {
    "path": "src/utils/pageTitle.js",
    "chars": 289,
    "preview": "import { title } from '@/config'\n\n/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 设置标题\n * @pa"
  },
  {
    "path": "src/utils/permission.js",
    "chars": 491,
    "preview": "import store from '@/store'\n\n/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 检查权限\n * @param v"
  },
  {
    "path": "src/utils/request.js",
    "chars": 3155,
    "preview": "import Vue from 'vue'\nimport axios from 'axios'\nimport {\n  baseURL,\n  contentType,\n  debounce,\n  invalidCode,\n  noPermis"
  },
  {
    "path": "src/utils/static.js",
    "chars": 1240,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com\n * @description 导入所有 controller 模块,浏览器环境中自动输出controller文件夹下Mock接口,请勿修改。\n */\ni"
  },
  {
    "path": "src/utils/vab.js",
    "chars": 3702,
    "preview": "import { loadingText, messageDuration, title } from '@/config'\nimport * as lodash from 'lodash'\nimport { Loading, Messag"
  },
  {
    "path": "src/utils/validate.js",
    "chars": 4919,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description 判读是否为外链\n * @param path\n * @returns {boolean}\n"
  },
  {
    "path": "src/views/401.vue",
    "chars": 6765,
    "preview": "<template>\n  <div class=\"error-container\">\n    <div class=\"error-content\">\n      <el-row :gutter=\"20\">\n        <el-col :"
  },
  {
    "path": "src/views/404.vue",
    "chars": 6776,
    "preview": "<template>\n  <div class=\"error-container\">\n    <div class=\"error-content\">\n      <el-row :gutter=\"20\">\n        <el-col :"
  },
  {
    "path": "src/views/index/index.vue",
    "chars": 3665,
    "preview": "<template>\n  <div class=\"more-container\">\n    <el-row :gutter=\"20\">\n      <el-col :xs=\"24\" :sm=\"24\" :md=\"24\" :lg=\"8\" :xl"
  },
  {
    "path": "src/views/login/index.vue",
    "chars": 7907,
    "preview": "<template>\n  <div class=\"login-container\">\n    <el-alert\n      v-if=\"nodeEnv !== 'development'\"\n      title=\"beautiful b"
  },
  {
    "path": "src/views/register/index.vue",
    "chars": 9304,
    "preview": "<template>\n  <div class=\"register-container\">\n    <el-alert\n      v-if=\"nodeEnv !== 'development'\"\n      title=\"beautifu"
  },
  {
    "path": "vab-icon/package.json",
    "chars": 84,
    "preview": "{\n  \"name\": \"vab-icon\",\n  \"version\": \"0.0.1\",\n  \"main\": \"lib/vab-icon.umd.min.js\"\n}\n"
  },
  {
    "path": "vue.config.js",
    "chars": 4989,
    "preview": "/**\n * @author chuzhixin 1204505056@qq.com (不想保留author可删除)\n * @description cli配置\n */\n\nconst path = require('path')\nconst"
  },
  {
    "path": "webstorm.config.js",
    "chars": 112,
    "preview": "'use strict'\nconst webpackConfig = require('@vue/cli-service/webpack.config.js')\nmodule.exports = webpackConfig\n"
  }
]

About this extraction

This page contains the full source code of the chuzhixin/vue-admin-beautiful-template GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 114 files (281.4 KB), approximately 82.2k tokens, and a symbol index with 108 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!