初志鑫 4 yıl önce
işleme
19d1a93109
100 değiştirilmiş dosya ile 8839 ekleme ve 0 silme
  1. 4 0
      .browserslistrc
  2. 12 0
      .editorconfig
  3. 2 0
      .env.development
  4. 1 0
      .env.preview
  5. 5 0
      .eslintignore
  6. 26 0
      .eslintrc.js
  7. 10 0
      .gitattributes
  8. 2 0
      .github/FUNDING.yml
  9. 18 0
      .gitignore
  10. 35 0
      .stylelintrc.js
  11. 48 0
      .vscode/settings.json
  12. 373 0
      LICENSE
  13. 277 0
      README.md
  14. 41 0
      README_DClOUD.md
  15. 21 0
      SECURITY.md
  16. 3 0
      babel.config.js
  17. 17 0
      deploy.sh
  18. 70 0
      http/mock.http
  19. 41 0
      mock/controller/ad.js
  20. 163 0
      mock/controller/changeLog.js
  21. 328 0
      mock/controller/colorfulIcon.js
  22. 42 0
      mock/controller/goodsDetail.js
  23. 43 0
      mock/controller/goodsList.js
  24. 989 0
      mock/controller/icon.js
  25. 51 0
      mock/controller/menuManagement.js
  26. 40 0
      mock/controller/notice.js
  27. 42 0
      mock/controller/personalCenter.js
  28. 2296 0
      mock/controller/remixIcon.js
  29. 55 0
      mock/controller/roleManagement.js
  30. 426 0
      mock/controller/router.js
  31. 88 0
      mock/controller/table.js
  32. 54 0
      mock/controller/tree.js
  33. 14 0
      mock/controller/upload.js
  34. 95 0
      mock/controller/user.js
  35. 70 0
      mock/controller/userManagement.js
  36. 35 0
      mock/index.js
  37. 94 0
      mock/mockServer.js
  38. 53 0
      mock/static.js
  39. 39 0
      mock/utils/index.js
  40. 121 0
      package.json
  41. 13 0
      plopfile.js
  42. 5 0
      postcss.config.js
  43. 15 0
      prettier.config.js
  44. BIN
      public/favicon.ico
  45. BIN
      public/favicon_backup.ico
  46. 47 0
      public/index.html
  47. 96 0
      public/static/css/loading.css
  48. 13 0
      push.sh
  49. 12 0
      src/App.vue
  50. 9 0
      src/api/ad.js
  51. 9 0
      src/api/changeLog.js
  52. 9 0
      src/api/colorfulIcon.js
  53. 25 0
      src/api/face.js
  54. 20 0
      src/api/github.js
  55. 9 0
      src/api/goodsDetail.js
  56. 9 0
      src/api/goodsList.js
  57. 9 0
      src/api/icon.js
  58. 25 0
      src/api/menuManagement.js
  59. 8 0
      src/api/notice.js
  60. 25 0
      src/api/personalCenter.js
  61. 8 0
      src/api/publicKey.js
  62. 9 0
      src/api/remixIcon.js
  63. 25 0
      src/api/roleManagement.js
  64. 9 0
      src/api/router.js
  65. 25 0
      src/api/table.js
  66. 9 0
      src/api/tree.js
  67. 37 0
      src/api/user.js
  68. 25 0
      src/api/userManagement.js
  69. BIN
      src/assets/comparison/left.jpg
  70. BIN
      src/assets/comparison/right.jpg
  71. BIN
      src/assets/error_images/401.png
  72. BIN
      src/assets/error_images/404.png
  73. BIN
      src/assets/error_images/cloud.png
  74. BIN
      src/assets/ewm.png
  75. BIN
      src/assets/login_images/background.jpg
  76. BIN
      src/assets/qr_logo/lqr_logo.png
  77. 17 0
      src/colorfulIcon/index.js
  78. 1 0
      src/colorfulIcon/svg/alphabetical_sorting.svg
  79. 22 0
      src/colorfulIcon/svg/vab.svg
  80. 65 0
      src/components/ColorfullIcon/index.vue
  81. 111 0
      src/components/JsonEditor/index.vue
  82. 69 0
      src/components/RemixIcon/index.vue
  83. 201 0
      src/components/SelectTree/index.vue
  84. 160 0
      src/components/UploadExcel/index.vue
  85. 191 0
      src/components/VabCharge/index.vue
  86. 92 0
      src/components/VabImage/index.vue
  87. 313 0
      src/components/VabProfile/index.vue
  88. 44 0
      src/components/VabQrCode/index.vue
  89. 20 0
      src/components/VabQueryForm/VabQueryFormBottomPanel.vue
  90. 25 0
      src/components/VabQueryForm/VabQueryFormLeftPanel.vue
  91. 25 0
      src/components/VabQueryForm/VabQueryFormRightPanel.vue
  92. 20 0
      src/components/VabQueryForm/VabQueryFormTopPanel.vue
  93. 63 0
      src/components/VabQueryForm/index.vue
  94. 124 0
      src/components/VabQuill/index.vue
  95. 81 0
      src/components/VabSnow/index.vue
  96. 95 0
      src/components/VabSticky/index.vue
  97. 267 0
      src/components/VabUpload/index.vue
  98. 85 0
      src/config/permission.js
  99. 99 0
      src/config/settings.js
  100. 0 0
      src/directive/drag/drag.js

+ 4 - 0
.browserslistrc

@@ -0,0 +1,4 @@
+> 1%
+last 2 versions
+not dead
+

+ 12 - 0
.editorconfig

@@ -0,0 +1,12 @@
+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

+ 2 - 0
.env.development

@@ -0,0 +1,2 @@
+NODE_ENV = development
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 1 - 0
.env.preview

@@ -0,0 +1 @@
+NODE_ENV = preview

+ 5 - 0
.eslintignore

@@ -0,0 +1,5 @@
+src/assets
+src/icons
+public
+dist
+node_modules

+ 26 - 0
.eslintrc.js

@@ -0,0 +1,26 @@
+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,
+      },
+    },
+  ],
+};

+ 10 - 0
.gitattributes

@@ -0,0 +1,10 @@
+*.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

+ 2 - 0
.github/FUNDING.yml

@@ -0,0 +1,2 @@
+patreon: chuzhixin
+

+ 18 - 0
.gitignore

@@ -0,0 +1,18 @@
+.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

+ 35 - 0
.stylelintrc.js

@@ -0,0 +1,35 @@
+module.exports = {
+  extends: ["stylelint-config-standard", "stylelint-config-recess-order"],
+  rules: {
+    "at-rule-no-unknown": [
+      true,
+      {
+        ignoreAtRules: [
+          "mixin",
+          "extend",
+          "content",
+          "include",
+          "for",
+          "function",
+          "return",
+        ],
+      },
+    ],
+    "selector-pseudo-element-no-unknown": [
+      true,
+      {
+        ignorePseudoElements: ["v-deep"],
+      },
+    ],
+    "selector-pseudo-class-no-unknown": [
+      true,
+      {
+        ignorePseudoClasses: ["export"],
+      },
+    ],
+    indentation: 2,
+    "no-descending-specificity": null,
+    "declaration-colon-newline-after": null,
+  },
+  ignoreFiles: ["**/*.js", "dist/*.*", "node_modules", "**/*.ts"],
+};

+ 48 - 0
.vscode/settings.json

@@ -0,0 +1,48 @@
+{
+  "[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,
+  "stylelint.enable": true,
+  "css.validate": false,
+  "less.validate": false,
+  "scss.validate": false,
+  "stylelint.autoFixOnSave": 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.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
+}

+ 373 - 0
LICENSE

@@ -0,0 +1,373 @@
+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.

Dosya farkı çok büyük olduğundan ihmal edildi
+ 277 - 0
README.md


+ 41 - 0
README_DClOUD.md

@@ -0,0 +1,41 @@
+## 本项目地址
+
+#### - [🚀 演示地址 1 vue-admin-beautiful ](https://chu1204505056.gitee.io/vue-admin-beautiful/)
+
+#### - [🚀 演示地址 2 vue-admin-beautiful ](http://beautiful.panm.cn/vue-admin-beautiful/)
+
+#### - [🚀clever 版本 演示地址 vue-admin-beautiful ](http://beautiful.panm.cn/vue-admin-clever/)
+
+#### - [🚀 开源地址,感谢 star](https://github.com/chuzhixin/vue-admin-beautiful/)
+
+#### 学习讨论 QQ 群:972435319 群内提供基础版、集成版、clever 版本、详细文档与视频教程
+
+#### 付费群
+
+![img](https://chu1204505056.gitee.io/byui-bookmarks/img/ewm.png)
+
+## 友情链接
+
+#### - [uView 文档(超棒的移动跨端框架,文档详细,上手容易)](https://uviewui.com/)
+
+#### - [uView 开源地址(uView UI,是 uni-app 生态优秀的 UI 框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水)](https://github.com/YanxinNet/uView/)
+
+#### - [luch-request(基于 Promise 开发的 uni-app 跨平台、项目级别的请求库,它有更小的体积,易用的 api,方便简单的自定义能力)](https://www.quanzhan.co/luch-request/)
+
+## 运行步骤,严格按步骤来
+
+```bash
+
+# 进入项目目录
+cd vue-admin-beautiful
+# 安装依赖,一定要cnpm i,别听网上乱七八糟的回答,本项目始终基于最新的package版本,cnpm不会出现任何问题,置于怎么安装cnpm自行百度
+cnpm i
+# 本地开发 启动项目
+cnpm run serve
+```
+
+#### <font color="red">已付费置顶 烦请小号刷差评的放过 我也需要养家糊口 也祝您财运亨通 好不好用请看演示地址 保留版权信息可免费商用(页面所有版权信息不付费也可完全删除) ,群内提供详细的视频与文档教程,由于置顶费用较高,如有需要在本页加广告的朋友可以联系我 QQ 1204505056</font>
+
+#### github 标星增长量统计
+
+[![Stargazers over time](https://starcharts.herokuapp.com/chuzhixin/vue-admin-beautiful.svg)](https://github.com/chuzhixin/vue-admin-beautiful)

+ 21 - 0
SECURITY.md

@@ -0,0 +1,21 @@
+# Security Policy
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 5.1.x   | :white_check_mark: |
+| 5.0.x   | :x:                |
+| 4.0.x   | :white_check_mark: |
+| < 4.0   | :x:                |
+
+## Reporting a Vulnerability
+
+Use this section to tell people how to report a vulnerability.
+
+Tell them where to go, how often they can expect to get an update on a
+reported vulnerability, what to expect if the vulnerability is accepted or
+declined, etc.

+ 3 - 0
babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  presets: ["@vue/cli-plugin-babel/preset"],
+};

+ 17 - 0
deploy.sh

@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -e
+npm run build:preview
+cd dist
+touch .nojekyll
+git init
+git add -A
+git commit -m 'deploy'
+git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-beautiful.git" master:gh-pages
+start "https://gitee.com/chu1204505056/vue-admin-beautiful/pages"
+git push -f "https://${access_token}@github.com/chuzhixin/vue-admin-beautiful.git" master:gh-pages
+cd -
+exec /bin/bash
+
+
+
+

+ 70 - 0
http/mock.http

@@ -0,0 +1,70 @@
+
+###/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
+###
+
+###/userInfo###
+POST http://localhost:80/mock-server/userInfo
+Content-Type: application/x-www-form-urlencoded
+###

+ 41 - 0
mock/controller/ad.js

@@ -0,0 +1,41 @@
+const data = [
+  {
+    title:
+      "作为一个程序员,我迄今为止最骄傲的事情:2020年7月10日,vue/cli4作者蒋豪群采纳了我的对sass-loader 9.0全局注入变量的文档修改建议,以后全世界都能看到我的名字了,这远比vue-admin-beautiful更让我有成就感,感谢shaonialife的帮助。",
+    url: "https://github.com/vuejs/vue-cli/blob/master/docs/zh/guide/css.md",
+  },
+  {
+    title:
+      "近日发现一个名为OKMG芒果源码的网站公然出售vue-admin-beautiful的开源代码,在此向大家说明,框架开源版本永久免费,请勿上当受骗。",
+    url:
+      "//shang.qq.com/wpa/qunwpa?idkey=14f123ac6d4ef3122bbb60d4693f1d8c951a50be2296951efb12d5ab1642f36b",
+  },
+  {
+    title: "uView UI:全面的组件和便捷的工具会让您信手拈来,如鱼得水。",
+    url: "https://uviewui.com/",
+  },
+  {
+    title:
+      "认认真真编程,踏踏实实做人;静坐常思己过,闲谈不论人非;希望使用vue-admin-beautiful框架的每个人,无论过程怎样,结局都是美好的。",
+    url:
+      "//shang.qq.com/wpa/qunwpa?idkey=14f123ac6d4ef3122bbb60d4693f1d8c951a50be2296951efb12d5ab1642f36b",
+  },
+  {
+    title: "vue-admin-beautiful前端讨论群-1:972435319",
+    url:
+      "//shang.qq.com/wpa/qunwpa?idkey=14f123ac6d4ef3122bbb60d4693f1d8c951a50be2296951efb12d5ab1642f36b",
+  },
+];
+export default [
+  {
+    url: "/ad/getList",
+    type: "get",
+    response: () => {
+      return {
+        code: 200,
+        msg: "success",
+        data,
+      };
+    },
+  },
+];

+ 163 - 0
mock/controller/changeLog.js

@@ -0,0 +1,163 @@
+const data = [
+  {
+    content: "在github上获得了第一个star,感恩一位名叫Bequiet2014的github用户",
+    timestamp: "2020-03-23",
+  },
+  {
+    content: "增加更换主题功能",
+    timestamp: "2020-04-10",
+  },
+  {
+    content: "大幅精简代码",
+    timestamp: "2020-04-14",
+  },
+  {
+    content: "修复群友反馈的bug",
+    timestamp: "2020-04-16",
+  },
+  {
+    content: "剔除maptalks",
+    timestamp: "2020-04-17",
+  },
+  {
+    content:
+      "换行符统一修改为lf 支持苹果 linux windows协同开发 强制开启最严格eslint规则 不要哭 严格是有好处的",
+    timestamp: "2020-04-17",
+  },
+  {
+    content: "彻底完成手机端适配,记录这一天熬夜到了晚上三点",
+    timestamp: "2020-04-18",
+  },
+  {
+    content:
+      "删除babel-polyfill 提高打包速度 减少压缩体积(放弃ie是这个项目做出的最伟大的决定)",
+    timestamp: "2020-04-18",
+  },
+  {
+    content: "源码精简至800k",
+    timestamp: "2020-04-19",
+  },
+  {
+    content: "添加视频播放器组件",
+    timestamp: "2020-04-20",
+  },
+  {
+    content: "修复路由懒加载 完善主题配色",
+    timestamp: "2020-04-22",
+  },
+  {
+    content: "修复全局axios拦截 加快动画展示效果 修改登录页样式",
+    timestamp: "2020-04-24",
+  },
+  {
+    content: "简化权限与登录逻辑 更新mockServer",
+    timestamp: "2020-04-25",
+  },
+  {
+    content: "优化登录退出逻辑 代码更清晰 退出不再重载网页 改为重载路由形式",
+    timestamp: "2020-04-26",
+  },
+  {
+    content: "无端的指责只会让我更加努力 修复sidebar 简化permission",
+    timestamp: "2020-04-28",
+  },
+  {
+    content: "又是一个深夜 实现了表格增删改查的一键生成",
+    timestamp: "2020-04-30",
+  },
+  {
+    content: "大幅优化tagsview标签动画",
+    timestamp: "2020-05-02",
+  },
+  {
+    content: "三种图标组件实现mock模拟分页",
+    timestamp: "2020-05-03",
+  },
+  {
+    content: "添加了markdown编辑器组件",
+    timestamp: "2020-05-04",
+  },
+  {
+    content: "添加stylelint-plus自动规整排序样式",
+    timestamp: "2020-05-06",
+  },
+  {
+    content: "添加商城模板",
+    timestamp: "2020-05-12",
+  },
+  {
+    content: "github标星超过1000 感恩",
+    timestamp: "2020-05-13",
+  },
+  {
+    content: "添加验证码组件",
+    timestamp: "2020-05-14",
+  },
+  {
+    content: "修复横向菜单bug",
+    timestamp: "2020-05-16",
+  },
+  {
+    content: "又被人骂了 挺好的 让我下定决心重写了tagsBar",
+    timestamp: "2020-05-20",
+  },
+  {
+    content: "仿ant-design 添加雪花屏",
+    timestamp: "2020-05-26",
+  },
+  {
+    content: "添加人员管理模块",
+    timestamp: "2020-06-02",
+  },
+  {
+    content: "github标星超过2000 感恩",
+    timestamp: "2020-06-03",
+  },
+  {
+    content: "添加炫酷地图组件",
+    timestamp: "2020-06-11",
+  },
+  {
+    content: "抽离更多公共配置,框架使用更顺手",
+    timestamp: "2020-06-19",
+  },
+  {
+    content: "彻底完成了tagsbar多标签页的重构",
+    timestamp: "2020-06-22",
+  },
+  {
+    content: "感恩github标星过3.0K 祝大家端午节快乐",
+    timestamp: "2020-06-25",
+  },
+  {
+    content: "彻底重构了SideBar与TopBar 大幅精简dom渲染逻辑 全球首发",
+    timestamp: "2020-06-25",
+  },
+  {
+    content: "添加菜单管理",
+    timestamp: "2020-07-7",
+  },
+  {
+    content: "首次采用sass-loader 9.0写法,感谢github用户 shaonialife",
+    timestamp: "2020-07-7",
+  },
+  {
+    content: "添加vue-amap组件",
+    timestamp: "2020-07-11",
+  },
+];
+
+export default [
+  {
+    url: "/changeLog/getList",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: 999,
+        data: data,
+      };
+    },
+  },
+];

+ 328 - 0
mock/controller/colorfulIcon.js

@@ -0,0 +1,328 @@
+const data = [
+  "alphabetical_sorting",
+  "advance",
+  "address_book",
+  "alphabetical_sorting",
+  "advertising",
+  "alarm_clock",
+  "area_chart",
+  "approval",
+  "answers",
+  "approve",
+  "assistant",
+  "audio_file",
+  "automotive",
+  "automatic",
+  "bad_decision",
+  "bar_chart",
+  "bearish",
+  "biomass",
+  "biohazard",
+  "binoculars",
+  "bookmark",
+  "briefcase",
+  "biotech",
+  "broken_link",
+  "business",
+  "bullish",
+  "business_contact",
+  "businesswoman",
+  "cable_release",
+  "calculator",
+  "businessman",
+  "calendar",
+  "butting_in",
+  "call_transfer",
+  "callback",
+  "camcorder",
+  "camera",
+  "camcorder_pro",
+  "cancel",
+  "camera_addon",
+  "camera_identificatio",
+  "capacitor",
+  "candle_sticks",
+  "checkmark",
+  "circuit",
+  "charge_battery",
+  "clear_filters",
+  "clapperboard",
+  "clock",
+  "close_up_mode",
+  "collaboration",
+  "cell_phone",
+  "collapse",
+  "collect",
+  "cloth",
+  "combo_chart",
+  "comments",
+  "conference_call",
+  "compact_camera",
+  "contacts",
+  "copyleft",
+  "copyright",
+  "crystal_oscillator",
+  "cursor",
+  "currency_exchange",
+  "customer_support",
+  "dam",
+  "data_backup",
+  "data_configuration",
+  "data_encryption",
+  "data_protection",
+  "data_recovery",
+  "database",
+  "data_sheet",
+  "debt",
+  "decision",
+  "delete_column",
+  "delete_database",
+  "department",
+  "delete_row",
+  "deployment",
+  "dislike",
+  "disapprove",
+  "disclaimer",
+  "display",
+  "document",
+  "do_not_insert",
+  "do_not_mix",
+  "do_not_inhale",
+  "donate",
+  "down",
+  "doughnut_chart",
+  "down_left",
+  "down_right",
+  "download",
+  "edit_image",
+  "electrical_sensor",
+  "electrical_threshold",
+  "electricity",
+  "electro_devices",
+  "electronics",
+  "empty_battery",
+  "empty_filter",
+  "empty_trash",
+  "end_call",
+  "engineering",
+  "entering_heaven_aliv",
+  "expand",
+  "export",
+  "expired",
+  "factory",
+  "factory_breakdown",
+  "external",
+  "faq",
+  "feed_in",
+  "file",
+  "feedback",
+  "film",
+  "filled_filter",
+  "filing_cabinet",
+  "film_reel",
+  "flash_auto",
+  "fine_print",
+  "flash_off",
+  "flash_on",
+  "flow_chart",
+  "folder",
+  "frame",
+  "full_battery",
+  "full_trash",
+  "gallery",
+  "generic_sorting_asc",
+  "generic_sorting_desc",
+  "genealogy",
+  "globe",
+  "good_decision",
+  "headset",
+  "grid",
+  "graduation_cap",
+  "heat_map",
+  "high_priority",
+  "high_battery",
+  "image_file",
+  "home",
+  "idea",
+  "import",
+  "in_transit",
+  "integrated_webcam",
+  "inspection",
+  "invite",
+  "internal",
+  "ipad",
+  "info",
+  "iphone",
+  "kindle",
+  "key",
+  "landscape",
+  "left",
+  "left_down",
+  "left_up",
+  "leave",
+  "like_placeholder",
+  "light_at_the_end_of_",
+  "library",
+  "line_chart",
+  "link",
+  "like",
+  "lock",
+  "list",
+  "lock_landscape",
+  "low_battery",
+  "lock_portrait",
+  "low_priority",
+  "make_decision",
+  "medium_priority",
+  "manager",
+  "menu",
+  "middle_battery",
+  "minus",
+  "missed_call",
+  "mind_map",
+  "mms",
+  "multiple_cameras",
+  "money_transfer",
+  "music",
+  "multiple_devices",
+  "multiple_smartphones",
+  "multiple_inputs",
+  "negative_dynamic",
+  "neutral_decision",
+  "night_landscape",
+  "news",
+  "neutral_trading",
+  "night_portrait",
+  "no_idea",
+  "next",
+  "no_video",
+  "nook",
+  "ok",
+  "org_unit",
+  "opened_folder",
+  "old_time_camera",
+  "online_support",
+  "organization",
+  "package",
+  "paid",
+  "parallel_tasks",
+  "overtime",
+  "panorama",
+  "phone",
+  "phone_android",
+  "photo_reel",
+  "pie_chart",
+  "picture",
+  "planner",
+  "plus",
+  "podium_with_audience",
+  "podium_without_speak",
+  "podium_with_speaker",
+  "previous",
+  "portrait_mode",
+  "positive_dynamic",
+  "privacy",
+  "process",
+  "puzzle",
+  "questions",
+  "print",
+  "radar_plot",
+  "rating",
+  "ratings",
+  "reading",
+  "redo",
+  "reading_ebook",
+  "refresh",
+  "registered_trademark",
+  "right",
+  "reuse",
+  "remove_image",
+  "right_down",
+  "right_up",
+  "rotate_to_portrait",
+  "rules",
+  "rotate_camera",
+  "rotate_to_landscape",
+  "ruler",
+  "scatter_plot",
+  "search",
+  "safe",
+  "self_service_kiosk",
+  "selfie",
+  "serial_tasks",
+  "sales_performance",
+  "settings",
+  "services",
+  "share",
+  "shipped",
+  "sim_card",
+  "shop",
+  "service_mark",
+  "sim_card_chip",
+  "signature",
+  "smartphone_tablet",
+  "sound_recording_copy",
+  "sms",
+  "speaker",
+  "slr_back_side",
+  "start",
+  "stack_of_photos",
+  "statistics",
+  "sports_mode",
+  "support",
+  "synchronize",
+  "switch_camera",
+  "survey",
+  "tablet_android",
+  "template",
+  "trademark",
+  "todo_list",
+  "touchscreen_smartpho",
+  "timeline",
+  "tree_structure",
+  "undo",
+  "up_left",
+  "two_smartphones",
+  "unlock",
+  "up",
+  "up_right",
+  "upload",
+  "video_call",
+  "video_file",
+  "view_details",
+  "video_projector",
+  "vip",
+  "voice_presentation",
+  "webcam",
+  "voicemail",
+  "workflow",
+  "about",
+  "accept_database",
+  "add_image",
+  "add_column",
+  "add_database",
+  "add_row",
+];
+export default [
+  {
+    url: "/colorfulIcon/getList",
+    type: "post",
+    response: (config) => {
+      const { title, pageNo = 1, pageSize = 72 } = config.body;
+      let mockList = data.filter((item) => {
+        if (title && item.indexOf(title) < 0) return false;
+        return true;
+      });
+      const pageList = mockList.filter(
+        (item, index) =>
+          index < pageSize * pageNo && index >= pageSize * (pageNo - 1)
+      );
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: mockList.length,
+        data: pageList,
+      };
+    },
+  },
+];

+ 42 - 0
mock/controller/goodsDetail.js

@@ -0,0 +1,42 @@
+import { mock } from "mockjs";
+
+export default [
+  {
+    url: "/goodsDetail/getList",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: 999,
+        data: mock({
+          "data|10": [
+            {
+              id: "@id",
+            },
+          ],
+        }).data,
+      };
+    },
+  },
+  {
+    url: "/goodsDetail/doEdit",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟保存成功",
+      };
+    },
+  },
+  {
+    url: "/goodsDetail/doDelete",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟删除成功",
+      };
+    },
+  },
+];

+ 43 - 0
mock/controller/goodsList.js

@@ -0,0 +1,43 @@
+import { mock } from "mockjs";
+
+const List = [];
+const count = 999;
+let num = 0;
+for (let i = 0; i < count; i++) {
+  List.push(
+    mock({
+      uuid: "@uuid",
+      image: `https://picsum.photos/300/600?random=${num++}`,
+      title: "@ctitle",
+      description: "@csentence",
+      link: "https://www.baidu.com",
+      price: "@integer(100, 500)",
+      "status|1": [1, 0],
+      "isRecommend|1": [1, 0],
+    })
+  );
+}
+
+export default [
+  {
+    url: "/goodsList/getList",
+    type: "post",
+    response: (config) => {
+      const { title = "", pageNo = 1, pageSize = 20 } = config.body;
+      let mockList = List.filter((item) => {
+        if (title && item.title.indexOf(title) < 0) return false;
+        return true;
+      });
+      const pageList = mockList.filter(
+        (item, index) =>
+          index < pageSize * pageNo && index >= pageSize * (pageNo - 1)
+      );
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: count,
+        data: pageList,
+      };
+    },
+  },
+];

+ 989 - 0
mock/controller/icon.js

@@ -0,0 +1,989 @@
+const data = [
+  "ad",
+  "address-book",
+  "address-card",
+  "adjust",
+  "air-freshener",
+  "align-center",
+  "align-justify",
+  "align-left",
+  "align-right",
+  "allergies",
+  "ambulance",
+  "american-sign-language-interpreting",
+  "anchor",
+  "angle-double-down",
+  "angle-double-left",
+  "angle-double-right",
+  "angle-double-up",
+  "angle-down",
+  "angle-left",
+  "angle-right",
+  "angle-up",
+  "angry",
+  "ankh",
+  "apple-alt",
+  "archive",
+  "archway",
+  "arrow-alt-circle-down",
+  "arrow-alt-circle-left",
+  "arrow-alt-circle-right",
+  "arrow-alt-circle-up",
+  "arrow-circle-down",
+  "arrow-circle-left",
+  "arrow-circle-right",
+  "arrow-circle-up",
+  "arrow-down",
+  "arrow-left",
+  "arrow-right",
+  "arrow-up",
+  "arrows-alt",
+  "arrows-alt-h",
+  "arrows-alt-v",
+  "assistive-listening-systems",
+  "asterisk",
+  "at",
+  "atlas",
+  "atom",
+  "audio-description",
+  "award",
+  "baby",
+  "baby-carriage",
+  "backspace",
+  "backward",
+  "bacon",
+  "bahai",
+  "balance-scale",
+  "balance-scale-left",
+  "balance-scale-right",
+  "ban",
+  "band-aid",
+  "barcode",
+  "bars",
+  "baseball-ball",
+  "basketball-ball",
+  "bath",
+  "battery-empty",
+  "battery-full",
+  "battery-half",
+  "battery-quarter",
+  "battery-three-quarters",
+  "bed",
+  "beer",
+  "bell",
+  "bell-slash",
+  "bezier-curve",
+  "bible",
+  "bicycle",
+  "biking",
+  "binoculars",
+  "biohazard",
+  "birthday-cake",
+  "blender",
+  "blender-phone",
+  "blind",
+  "blog",
+  "bold",
+  "bolt",
+  "bomb",
+  "bone",
+  "bong",
+  "book",
+  "book-dead",
+  "book-medical",
+  "book-open",
+  "book-reader",
+  "bookmark",
+  "border-all",
+  "border-none",
+  "border-style",
+  "bowling-ball",
+  "box",
+  "box-open",
+  "boxes",
+  "braille",
+  "brain",
+  "bread-slice",
+  "briefcase",
+  "briefcase-medical",
+  "broadcast-tower",
+  "broom",
+  "brush",
+  "bug",
+  "building",
+  "bullhorn",
+  "bullseye",
+  "burn",
+  "bus",
+  "bus-alt",
+  "business-time",
+  "calculator",
+  "calendar",
+  "calendar-alt",
+  "calendar-check",
+  "calendar-day",
+  "calendar-minus",
+  "calendar-plus",
+  "calendar-times",
+  "calendar-week",
+  "camera",
+  "camera-retro",
+  "campground",
+  "candy-cane",
+  "cannabis",
+  "capsules",
+  "car",
+  "car-alt",
+  "car-battery",
+  "car-crash",
+  "car-side",
+  "caravan",
+  "caret-down",
+  "caret-left",
+  "caret-right",
+  "caret-square-down",
+  "caret-square-left",
+  "caret-square-right",
+  "caret-square-up",
+  "caret-up",
+  "carrot",
+  "cart-arrow-down",
+  "cart-plus",
+  "cash-register",
+  "cat",
+  "certificate",
+  "chair",
+  "chalkboard",
+  "chalkboard-teacher",
+  "charging-station",
+  "chart-area",
+  "chart-bar",
+  "chart-line",
+  "chart-pie",
+  "check",
+  "check-circle",
+  "check-double",
+  "check-square",
+  "cheese",
+  "chess",
+  "chess-bishop",
+  "chess-board",
+  "chess-king",
+  "chess-knight",
+  "chess-pawn",
+  "chess-queen",
+  "chess-rook",
+  "chevron-circle-down",
+  "chevron-circle-left",
+  "chevron-circle-right",
+  "chevron-circle-up",
+  "chevron-down",
+  "chevron-left",
+  "chevron-right",
+  "chevron-up",
+  "child",
+  "church",
+  "circle",
+  "circle-notch",
+  "city",
+  "clinic-medical",
+  "clipboard",
+  "clipboard-check",
+  "clipboard-list",
+  "clock",
+  "clone",
+  "closed-captioning",
+  "cloud",
+  "cloud-download-alt",
+  "cloud-meatball",
+  "cloud-moon",
+  "cloud-moon-rain",
+  "cloud-rain",
+  "cloud-showers-heavy",
+  "cloud-sun",
+  "cloud-sun-rain",
+  "cloud-upload-alt",
+  "cocktail",
+  "code",
+  "code-branch",
+  "coffee",
+  "cog",
+  "cogs",
+  "coins",
+  "columns",
+  "comment",
+  "comment-alt",
+  "comment-dollar",
+  "comment-dots",
+  "comment-medical",
+  "comment-slash",
+  "comments",
+  "comments-dollar",
+  "compact-disc",
+  "compass",
+  "compress",
+  "compress-alt",
+  "compress-arrows-alt",
+  "concierge-bell",
+  "cookie",
+  "cookie-bite",
+  "copy",
+  "copyright",
+  "couch",
+  "credit-card",
+  "crop",
+  "crop-alt",
+  "cross",
+  "crosshairs",
+  "crow",
+  "crown",
+  "crutch",
+  "cube",
+  "cubes",
+  "cut",
+  "database",
+  "deaf",
+  "democrat",
+  "desktop",
+  "dharmachakra",
+  "diagnoses",
+  "dice",
+  "dice-d20",
+  "dice-d6",
+  "dice-five",
+  "dice-four",
+  "dice-one",
+  "dice-six",
+  "dice-three",
+  "dice-two",
+  "digital-tachograph",
+  "directions",
+  "divide",
+  "dizzy",
+  "dna",
+  "dog",
+  "dollar-sign",
+  "dolly",
+  "dolly-flatbed",
+  "donate",
+  "door-closed",
+  "door-open",
+  "dot-circle",
+  "dove",
+  "download",
+  "drafting-compass",
+  "dragon",
+  "draw-polygon",
+  "drum",
+  "drum-steelpan",
+  "drumstick-bite",
+  "dumbbell",
+  "dumpster",
+  "dumpster-fire",
+  "dungeon",
+  "edit",
+  "egg",
+  "eject",
+  "ellipsis-h",
+  "ellipsis-v",
+  "envelope",
+  "envelope-open",
+  "envelope-open-text",
+  "envelope-square",
+  "equals",
+  "eraser",
+  "ethernet",
+  "euro-sign",
+  "exchange-alt",
+  "exclamation",
+  "exclamation-circle",
+  "exclamation-triangle",
+  "expand",
+  "expand-alt",
+  "expand-arrows-alt",
+  "external-link-alt",
+  "external-link-square-alt",
+  "eye",
+  "eye-dropper",
+  "eye-slash",
+  "fan",
+  "fast-backward",
+  "fast-forward",
+  "fax",
+  "feather",
+  "feather-alt",
+  "female",
+  "fighter-jet",
+  "file",
+  "file-alt",
+  "file-archive",
+  "file-audio",
+  "file-code",
+  "file-contract",
+  "file-csv",
+  "file-download",
+  "file-excel",
+  "file-export",
+  "file-image",
+  "file-import",
+  "file-invoice",
+  "file-invoice-dollar",
+  "file-medical",
+  "file-medical-alt",
+  "file-pdf",
+  "file-powerpoint",
+  "file-prescription",
+  "file-signature",
+  "file-upload",
+  "file-video",
+  "file-word",
+  "fill",
+  "fill-drip",
+  "film",
+  "filter",
+  "fingerprint",
+  "fire",
+  "fire-alt",
+  "fire-extinguisher",
+  "first-aid",
+  "fish",
+  "fist-raised",
+  "flag",
+  "flag-checkered",
+  "flag-usa",
+  "flask",
+  "flushed",
+  "folder",
+  "folder-minus",
+  "folder-open",
+  "folder-plus",
+  "font",
+  "football-ball",
+  "forward",
+  "frog",
+  "frown",
+  "frown-open",
+  "funnel-dollar",
+  "futbol",
+  "gamepad",
+  "gas-pump",
+  "gavel",
+  "gem",
+  "genderless",
+  "ghost",
+  "gift",
+  "gifts",
+  "glass-cheers",
+  "glass-martini",
+  "glass-martini-alt",
+  "glass-whiskey",
+  "glasses",
+  "globe",
+  "globe-africa",
+  "globe-americas",
+  "globe-asia",
+  "globe-europe",
+  "golf-ball",
+  "gopuram",
+  "graduation-cap",
+  "greater-than",
+  "greater-than-equal",
+  "grimace",
+  "grin",
+  "grin-alt",
+  "grin-beam",
+  "grin-beam-sweat",
+  "grin-hearts",
+  "grin-squint",
+  "grin-squint-tears",
+  "grin-stars",
+  "grin-tears",
+  "grin-tongue",
+  "grin-tongue-squint",
+  "grin-tongue-wink",
+  "grin-wink",
+  "grip-horizontal",
+  "grip-lines",
+  "grip-lines-vertical",
+  "grip-vertical",
+  "guitar",
+  "h-square",
+  "hamburger",
+  "hammer",
+  "hamsa",
+  "hand-holding",
+  "hand-holding-heart",
+  "hand-holding-usd",
+  "hand-lizard",
+  "hand-middle-finger",
+  "hand-paper",
+  "hand-peace",
+  "hand-point-down",
+  "hand-point-left",
+  "hand-point-right",
+  "hand-point-up",
+  "hand-pointer",
+  "hand-rock",
+  "hand-scissors",
+  "hand-spock",
+  "hands",
+  "hands-helping",
+  "handshake",
+  "hanukiah",
+  "hard-hat",
+  "hashtag",
+  "hat-cowboy",
+  "hat-cowboy-side",
+  "hat-wizard",
+  "hdd",
+  "heading",
+  "headphones",
+  "headphones-alt",
+  "headset",
+  "heart",
+  "heart-broken",
+  "heartbeat",
+  "helicopter",
+  "highlighter",
+  "hiking",
+  "hippo",
+  "history",
+  "hockey-puck",
+  "holly-berry",
+  "home",
+  "horse",
+  "horse-head",
+  "hospital",
+  "hospital-alt",
+  "hospital-symbol",
+  "hot-tub",
+  "hotdog",
+  "hotel",
+  "hourglass",
+  "hourglass-end",
+  "hourglass-half",
+  "hourglass-start",
+  "house-damage",
+  "hryvnia",
+  "i-cursor",
+  "ice-cream",
+  "icicles",
+  "icons",
+  "id-badge",
+  "id-card",
+  "id-card-alt",
+  "igloo",
+  "image",
+  "images",
+  "inbox",
+  "indent",
+  "industry",
+  "infinity",
+  "info",
+  "info-circle",
+  "italic",
+  "jedi",
+  "joint",
+  "journal-whills",
+  "kaaba",
+  "key",
+  "keyboard",
+  "khanda",
+  "kiss",
+  "kiss-beam",
+  "kiss-wink-heart",
+  "kiwi-bird",
+  "landmark",
+  "language",
+  "laptop",
+  "laptop-code",
+  "laptop-medical",
+  "laugh",
+  "laugh-beam",
+  "laugh-squint",
+  "laugh-wink",
+  "layer-group",
+  "leaf",
+  "lemon",
+  "less-than",
+  "less-than-equal",
+  "level-down-alt",
+  "level-up-alt",
+  "life-ring",
+  "lightbulb",
+  "link",
+  "lira-sign",
+  "list",
+  "list-alt",
+  "list-ol",
+  "list-ul",
+  "location-arrow",
+  "lock",
+  "lock-open",
+  "long-arrow-alt-down",
+  "long-arrow-alt-left",
+  "long-arrow-alt-right",
+  "long-arrow-alt-up",
+  "low-vision",
+  "luggage-cart",
+  "magic",
+  "magnet",
+  "mail-bulk",
+  "male",
+  "map",
+  "map-marked",
+  "map-marked-alt",
+  "map-marker",
+  "map-marker-alt",
+  "map-pin",
+  "map-signs",
+  "marker",
+  "mars",
+  "mars-double",
+  "mars-stroke",
+  "mars-stroke-h",
+  "mars-stroke-v",
+  "mask",
+  "medal",
+  "medkit",
+  "meh",
+  "meh-blank",
+  "meh-rolling-eyes",
+  "memory",
+  "menorah",
+  "mercury",
+  "meteor",
+  "microchip",
+  "microphone",
+  "microphone-alt",
+  "microphone-alt-slash",
+  "microphone-slash",
+  "microscope",
+  "minus",
+  "minus-circle",
+  "minus-square",
+  "mitten",
+  "mobile",
+  "mobile-alt",
+  "money-bill",
+  "money-bill-alt",
+  "money-bill-wave",
+  "money-bill-wave-alt",
+  "money-check",
+  "money-check-alt",
+  "monument",
+  "moon",
+  "mortar-pestle",
+  "mosque",
+  "motorcycle",
+  "mountain",
+  "mouse",
+  "mouse-pointer",
+  "mug-hot",
+  "music",
+  "network-wired",
+  "neuter",
+  "newspaper",
+  "not-equal",
+  "notes-medical",
+  "object-group",
+  "object-ungroup",
+  "oil-can",
+  "om",
+  "otter",
+  "outdent",
+  "pager",
+  "paint-brush",
+  "paint-roller",
+  "palette",
+  "pallet",
+  "paper-plane",
+  "paperclip",
+  "parachute-box",
+  "paragraph",
+  "parking",
+  "passport",
+  "pastafarianism",
+  "paste",
+  "pause",
+  "pause-circle",
+  "paw",
+  "peace",
+  "pen",
+  "pen-alt",
+  "pen-fancy",
+  "pen-nib",
+  "pen-square",
+  "pencil-alt",
+  "pencil-ruler",
+  "people-carry",
+  "pepper-hot",
+  "percent",
+  "percentage",
+  "person-booth",
+  "phone",
+  "phone-alt",
+  "phone-slash",
+  "phone-square",
+  "phone-square-alt",
+  "phone-volume",
+  "photo-video",
+  "piggy-bank",
+  "pills",
+  "pizza-slice",
+  "place-of-worship",
+  "plane",
+  "plane-arrival",
+  "plane-departure",
+  "play",
+  "play-circle",
+  "plug",
+  "plus",
+  "plus-circle",
+  "plus-square",
+  "podcast",
+  "poll",
+  "poll-h",
+  "poo",
+  "poo-storm",
+  "poop",
+  "portrait",
+  "pound-sign",
+  "power-off",
+  "pray",
+  "praying-hands",
+  "prescription",
+  "prescription-bottle",
+  "prescription-bottle-alt",
+  "print",
+  "procedures",
+  "project-diagram",
+  "puzzle-piece",
+  "qrcode",
+  "question",
+  "question-circle",
+  "quidditch",
+  "quote-left",
+  "quote-right",
+  "quran",
+  "radiation",
+  "radiation-alt",
+  "rainbow",
+  "random",
+  "receipt",
+  "record-vinyl",
+  "recycle",
+  "redo",
+  "redo-alt",
+  "registered",
+  "remove-format",
+  "reply",
+  "reply-all",
+  "republican",
+  "restroom",
+  "retweet",
+  "ribbon",
+  "ring",
+  "road",
+  "robot",
+  "rocket",
+  "route",
+  "rss",
+  "rss-square",
+  "ruble-sign",
+  "ruler",
+  "ruler-combined",
+  "ruler-horizontal",
+  "ruler-vertical",
+  "running",
+  "rupee-sign",
+  "sad-cry",
+  "sad-tear",
+  "satellite",
+  "satellite-dish",
+  "save",
+  "school",
+  "screwdriver",
+  "scroll",
+  "sd-card",
+  "search",
+  "search-dollar",
+  "search-location",
+  "search-minus",
+  "search-plus",
+  "seedling",
+  "server",
+  "shapes",
+  "share",
+  "share-alt",
+  "share-alt-square",
+  "share-square",
+  "shekel-sign",
+  "shield-alt",
+  "ship",
+  "shipping-fast",
+  "shoe-prints",
+  "shopping-bag",
+  "shopping-basket",
+  "shopping-cart",
+  "shower",
+  "shuttle-van",
+  "sign",
+  "sign-in-alt",
+  "sign-language",
+  "sign-out-alt",
+  "signal",
+  "signature",
+  "sim-card",
+  "sitemap",
+  "skating",
+  "skiing",
+  "skiing-nordic",
+  "skull",
+  "skull-crossbones",
+  "slash",
+  "sleigh",
+  "sliders-h",
+  "smile",
+  "smile-beam",
+  "smile-wink",
+  "smog",
+  "smoking",
+  "smoking-ban",
+  "sms",
+  "snowboarding",
+  "snowflake",
+  "snowman",
+  "snowplow",
+  "socks",
+  "solar-panel",
+  "sort",
+  "sort-alpha-down",
+  "sort-alpha-down-alt",
+  "sort-alpha-up",
+  "sort-alpha-up-alt",
+  "sort-amount-down",
+  "sort-amount-down-alt",
+  "sort-amount-up",
+  "sort-amount-up-alt",
+  "sort-down",
+  "sort-numeric-down",
+  "sort-numeric-down-alt",
+  "sort-numeric-up",
+  "sort-numeric-up-alt",
+  "sort-up",
+  "spa",
+  "space-shuttle",
+  "spell-check",
+  "spider",
+  "spinner",
+  "splotch",
+  "spray-can",
+  "square",
+  "square-full",
+  "square-root-alt",
+  "stamp",
+  "star",
+  "star-and-crescent",
+  "star-half",
+  "star-half-alt",
+  "star-of-david",
+  "star-of-life",
+  "step-backward",
+  "step-forward",
+  "stethoscope",
+  "sticky-note",
+  "stop",
+  "stop-circle",
+  "stopwatch",
+  "store",
+  "store-alt",
+  "stream",
+  "street-view",
+  "strikethrough",
+  "stroopwafel",
+  "subscript",
+  "subway",
+  "suitcase",
+  "suitcase-rolling",
+  "sun",
+  "superscript",
+  "surprise",
+  "swatchbook",
+  "swimmer",
+  "swimming-pool",
+  "synagogue",
+  "sync",
+  "sync-alt",
+  "syringe",
+  "table",
+  "table-tennis",
+  "tablet",
+  "tablet-alt",
+  "tablets",
+  "tachometer-alt",
+  "tag",
+  "tags",
+  "tape",
+  "tasks",
+  "taxi",
+  "teeth",
+  "teeth-open",
+  "temperature-high",
+  "temperature-low",
+  "tenge",
+  "terminal",
+  "text-height",
+  "text-width",
+  "th",
+  "th-large",
+  "th-list",
+  "theater-masks",
+  "thermometer",
+  "thermometer-empty",
+  "thermometer-full",
+  "thermometer-half",
+  "thermometer-quarter",
+  "thermometer-three-quarters",
+  "thumbs-down",
+  "thumbs-up",
+  "thumbtack",
+  "ticket-alt",
+  "times",
+  "times-circle",
+  "tint",
+  "tint-slash",
+  "tired",
+  "toggle-off",
+  "toggle-on",
+  "toilet",
+  "toilet-paper",
+  "toolbox",
+  "tools",
+  "tooth",
+  "torah",
+  "torii-gate",
+  "tractor",
+  "trademark",
+  "traffic-light",
+  "trailer",
+  "train",
+  "tram",
+  "transgender",
+  "transgender-alt",
+  "trash",
+  "trash-alt",
+  "trash-restore",
+  "trash-restore-alt",
+  "tree",
+  "trophy",
+  "truck",
+  "truck-loading",
+  "truck-monster",
+  "truck-moving",
+  "truck-pickup",
+  "tshirt",
+  "tty",
+  "tv",
+  "umbrella",
+  "umbrella-beach",
+  "underline",
+  "undo",
+  "undo-alt",
+  "universal-access",
+  "university",
+  "unlink",
+  "unlock",
+  "unlock-alt",
+  "upload",
+  "user",
+  "user-alt",
+  "user-alt-slash",
+  "user-astronaut",
+  "user-check",
+  "user-circle",
+  "user-clock",
+  "user-cog",
+  "user-edit",
+  "user-friends",
+  "user-graduate",
+  "user-injured",
+  "user-lock",
+  "user-md",
+  "user-minus",
+  "user-ninja",
+  "user-nurse",
+  "user-plus",
+  "user-secret",
+  "user-shield",
+  "user-slash",
+  "user-tag",
+  "user-tie",
+  "user-times",
+  "users",
+  "users-cog",
+  "utensil-spoon",
+  "utensils",
+  "vector-square",
+  "venus",
+  "venus-double",
+  "venus-mars",
+  "vial",
+  "vials",
+  "video",
+  "video-slash",
+  "vihara",
+  "voicemail",
+  "volleyball-ball",
+  "volume-down",
+  "volume-mute",
+  "volume-off",
+  "volume-up",
+  "vote-yea",
+  "vr-cardboard",
+  "walking",
+  "wallet",
+  "warehouse",
+  "water",
+  "wave-square",
+  "weight",
+  "weight-hanging",
+  "wheelchair",
+  "wifi",
+  "wind",
+  "window-close",
+  "window-maximize",
+  "window-minimize",
+  "window-restore",
+  "wine-bottle",
+  "wine-glass",
+  "wine-glass-alt",
+  "won-sign",
+  "wrench",
+  "x-ray",
+  "yen-sign",
+  "yin-yang",
+];
+export default [
+  {
+    url: "/icon/getList",
+    type: "post",
+    response: (config) => {
+      const { title, pageNo = 1, pageSize = 72 } = config.body;
+      let mockList = data.filter((item) => {
+        if (title && item.indexOf(title) < 0) return false;
+        return true;
+      });
+      const pageList = mockList.filter(
+        (item, index) =>
+          index < pageSize * pageNo && index >= pageSize * (pageNo - 1)
+      );
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: mockList.length,
+        data: pageList,
+      };
+    },
+  },
+];

+ 51 - 0
mock/controller/menuManagement.js

@@ -0,0 +1,51 @@
+export default [
+  {
+    url: "/menuManagement/getTree",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: 999,
+        data: [
+          {
+            id: "root",
+            label: "全部角色",
+            children: [
+              {
+                id: "@id",
+                permission: "admin",
+                label: "admin角色",
+              },
+              {
+                id: "@id",
+                permission: "editor",
+                label: "editor角色",
+              },
+            ],
+          },
+        ],
+      };
+    },
+  },
+  {
+    url: "/menuManagement/doEdit",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟保存成功",
+      };
+    },
+  },
+  {
+    url: "/menuManagement/doDelete",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟删除成功",
+      };
+    },
+  },
+];

Dosya farkı çok büyük olduğundan ihmal edildi
+ 40 - 0
mock/controller/notice.js


+ 42 - 0
mock/controller/personalCenter.js

@@ -0,0 +1,42 @@
+import { mock } from "mockjs";
+
+export default [
+  {
+    url: "/personalCenter/getList",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: 999,
+        data: mock({
+          "data|10": [
+            {
+              id: "@id",
+            },
+          ],
+        }).data,
+      };
+    },
+  },
+  {
+    url: "/personalCenter/doEdit",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟保存成功",
+      };
+    },
+  },
+  {
+    url: "/personalCenter/doDelete",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟删除成功",
+      };
+    },
+  },
+];

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2296 - 0
mock/controller/remixIcon.js


+ 55 - 0
mock/controller/roleManagement.js

@@ -0,0 +1,55 @@
+import { mock } from "mockjs";
+const totalCount = 2;
+const List = [
+  {
+    id: "@id",
+    permission: "admin",
+  },
+  {
+    id: "@id",
+    permission: "editor",
+  },
+];
+export default [
+  {
+    url: "/roleManagement/getList",
+    type: "post",
+    response: (config) => {
+      const { title = "", pageNo = 1, pageSize = 20 } = config.body;
+      let mockList = List.filter((item) => {
+        if (title && item.title.indexOf(title) < 0) return false;
+        return true;
+      });
+      const pageList = mockList.filter(
+        (item, index) =>
+          index < pageSize * pageNo && index >= pageSize * (pageNo - 1)
+      );
+      return {
+        code: 200,
+        msg: "success",
+        totalCount,
+        data: pageList,
+      };
+    },
+  },
+  {
+    url: "/roleManagement/doEdit",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟保存成功",
+      };
+    },
+  },
+  {
+    url: "/roleManagement/doDelete",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟删除成功",
+      };
+    },
+  },
+];

+ 426 - 0
mock/controller/router.js

@@ -0,0 +1,426 @@
+const data = [
+  {
+    path: "/",
+    component: "Layout",
+    redirect: "index",
+    children: [
+      {
+        path: "index",
+        name: "Index",
+        component: "views/index/index",
+        meta: {
+          title: "首页",
+          icon: "home",
+          affix: true,
+        },
+      },
+    ],
+  },
+  {
+    path: "/personalCenter",
+    component: "Layout",
+    hidden: true,
+    redirect: "personalCenter",
+    children: [
+      {
+        path: "personalCenter",
+        name: "PersonalCenter",
+        component: "views/personalCenter/index",
+        meta: {
+          title: "个人中心",
+        },
+      },
+    ],
+  },
+  {
+    path: "/personnelManagement",
+    component: "Layout",
+    redirect: "noRedirect",
+    name: "PersonnelManagement",
+    meta: { title: "人员", icon: "users-cog", permissions: ["admin"] },
+    children: [
+      {
+        path: "userManagement",
+        name: "UserManagement",
+        component: "views/personnelManagement/userManagement/index",
+        meta: { title: "用户管理" },
+      },
+      {
+        path: "roleManagement",
+        name: "RoleManagement",
+        component: "views/personnelManagement/roleManagement/index",
+        meta: { title: "角色管理" },
+      },
+      {
+        path: "menuManagement",
+        name: "MenuManagement",
+        component: "views/personnelManagement/menuManagement/index",
+        meta: { title: "菜单管理", badge: "New" },
+      },
+    ],
+  },
+  {
+    path: "/vab",
+    component: "Layout",
+    redirect: "noRedirect",
+    name: "Vab",
+    alwaysShow: true,
+    meta: { title: "组件", icon: "cloud" },
+    children: [
+      {
+        path: "permissions",
+        name: "Permission",
+        component: "views/vab/permissions/index",
+        meta: {
+          title: "权限控制",
+          permissions: ["admin", "editor"],
+          badge: "New",
+        },
+      },
+      {
+        path: "icon",
+        component: "EmptyLayout",
+        redirect: "noRedirect",
+        name: "Icon",
+        meta: {
+          title: "图标",
+          permissions: ["admin"],
+        },
+        children: [
+          {
+            path: "awesomeIcon",
+            name: "AwesomeIcon",
+            component: "views/vab/icon/index",
+            meta: { title: "常规图标" },
+          },
+          {
+            path: "remixIcon",
+            name: "RemixIcon",
+            component: "views/vab/icon/remixIcon",
+            meta: { title: "小清新图标" },
+          },
+          {
+            path: "colorfulIcon",
+            name: "ColorfulIcon",
+            component: "views/vab/icon/colorfulIcon",
+            meta: { title: "多彩图标" },
+          },
+        ],
+      },
+      {
+        path: "table",
+        component: "EmptyLayout",
+        redirect: "noRedirect",
+        name: "Table",
+        meta: {
+          title: "表格",
+          permissions: ["admin"],
+        },
+        children: [
+          {
+            path: "comprehensiveTable",
+            name: "ComprehensiveTable",
+            component: "views/vab/table/index",
+            meta: { title: "综合表格" },
+          },
+          {
+            path: "inlineEditTable",
+            name: "InlineEditTable",
+            component: "views/vab/table/inlineEditTable",
+            meta: { title: "行内编辑" },
+          },
+        ],
+      },
+      {
+        path: "map",
+        name: "Map",
+        component: "views/vab/map/index",
+        meta: { title: "地图", permissions: ["admin"], badge: "Pro" },
+      },
+      {
+        path: "webSocket",
+        name: "WebSocket",
+        component: "views/vab/webSocket/index",
+        meta: { title: "webSocket", permissions: ["admin"] },
+      },
+      {
+        path: "form",
+        name: "Form",
+        component: "views/vab/form/index",
+        meta: { title: "表单", permissions: ["admin"] },
+      },
+      {
+        path: "element",
+        name: "Element",
+        component: "views/vab/element/index",
+        meta: { title: "常用组件", permissions: ["admin"] },
+      },
+      {
+        path: "tree",
+        name: "Tree",
+        component: "views/vab/tree/index",
+        meta: { title: "树", permissions: ["admin"] },
+      },
+      {
+        path: "card",
+        name: "Card",
+        component: "views/vab/card/index",
+        meta: { title: "卡片", permissions: ["admin"] },
+      },
+
+      {
+        path: "betterScroll",
+        name: "BetterScroll",
+        component: "views/vab/betterScroll/index",
+        meta: {
+          title: "滚动侦测",
+          permissions: ["admin"],
+        },
+      },
+      {
+        path: "verify",
+        name: "Verify",
+        component: "views/vab/verify/index",
+        meta: { title: "验证码", permissions: ["admin"] },
+      },
+      {
+        path: "menu1",
+        component: "views/vab/nested/menu1/index",
+        name: "Menu1",
+        alwaysShow: true,
+        meta: {
+          title: "嵌套路由 1",
+          permissions: ["admin"],
+        },
+        children: [
+          {
+            path: "menu1-1",
+            name: "Menu1-1",
+            alwaysShow: true,
+            meta: { title: "嵌套路由 1-1" },
+            component: "views/vab/nested/menu1/menu1-1/index",
+
+            children: [
+              {
+                path: "menu1-1-1",
+                name: "Menu1-1-1",
+                meta: { title: "嵌套路由 1-1-1" },
+                component: "views/vab/nested/menu1/menu1-1/menu1-1-1/index",
+              },
+            ],
+          },
+        ],
+      },
+      {
+        path: "magnifier",
+        name: "Magnifier",
+        component: "views/vab/magnifier/index",
+        meta: { title: "放大镜", permissions: ["admin"] },
+      },
+      {
+        path: "echarts",
+        name: "Echarts",
+        component: "views/vab/echarts/index",
+        meta: { title: "图表", permissions: ["admin"] },
+      },
+
+      {
+        path: "loading",
+        name: "Loading",
+        component: "views/vab/loading/index",
+        meta: { title: "loading", permissions: ["admin"] },
+      },
+      {
+        path: "player",
+        name: "Player",
+        component: "views/vab/player/index",
+        meta: { title: "视频播放器", permissions: ["admin"] },
+      },
+      {
+        path: "markdownEditor",
+        name: "MarkdownEditor",
+        component: "views/vab/markdownEditor/index",
+        meta: { title: "markdown编辑器", permissions: ["admin"] },
+      },
+      {
+        path: "editor",
+        name: "Editor",
+        component: "views/vab/editor/index",
+        meta: { title: "富文本编辑器", permissions: ["admin"], badge: "New" },
+      },
+      {
+        path: "qrCode",
+        name: "QrCode",
+        component: "views/vab/qrCode/index",
+        meta: { title: "二维码", permissions: ["admin"] },
+      },
+      {
+        path: "backToTop",
+        name: "BackToTop",
+        component: "views/vab/backToTop/index",
+        meta: { title: "返回顶部", permissions: ["admin"] },
+      },
+      {
+        path: "lodash",
+        name: "Lodash",
+        component: "views/vab/lodash/index",
+        meta: { title: "lodash", permissions: ["admin"] },
+      },
+      {
+        path: "imgComparison",
+        name: "ImgComparison",
+        component: "views/vab/imgComparison/index",
+        meta: { title: "图像拖拽比对", permissions: ["admin"] },
+      },
+      {
+        path: "codeGenerator",
+        name: "CodeGenerator",
+        component: "views/vab/codeGenerator/index",
+        meta: { title: "代码生成机", permissions: ["admin"] },
+      },
+      {
+        path: "markdown",
+        name: "Markdown",
+        component: "views/vab/markdown/index",
+        meta: { title: "markdown阅读器", permissions: ["admin"] },
+      },
+      {
+        path: "smallComponents",
+        name: "SmallComponents",
+        component: "views/vab/smallComponents/index",
+        meta: { title: "小组件", permissions: ["admin"] },
+      },
+
+      {
+        path: "upload",
+        name: "Upload",
+        component: "views/vab/upload/index",
+        meta: { title: "上传", permissions: ["admin"] },
+      },
+      {
+        path: "excel",
+        component: "EmptyLayout",
+        redirect: "noRedirect",
+        name: "Excel",
+        meta: {
+          title: "Excel",
+          permissions: ["admin"],
+        },
+        children: [
+          {
+            path: "exportExcel",
+            component: "views/vab/excel/exportExcel",
+            name: "ExportExcel",
+            meta: { title: "导出Excel" },
+          },
+          {
+            path: "exportSelectedExcel",
+            component: "views/vab/excel/exportSelectExcel",
+            name: "ExportSelectedExcel",
+            meta: { title: "导出选中行" },
+          },
+          {
+            path: "exportMergeHeaderExcel",
+            component: "views/vab/excel/exportMergeHeaderExcel",
+            name: "ExportMergeHeaderExcel",
+            meta: { title: "导出合并" },
+          },
+          {
+            path: "uploadExcel",
+            component: "views/vab/excel/uploadExcel",
+            name: "UploadExcel",
+            meta: { title: "上传Excel" },
+          },
+        ],
+      },
+      {
+        path: "sticky",
+        name: "Sticky",
+        component: "views/vab/sticky/index",
+        meta: { title: "sticky吸附", permissions: ["admin"] },
+      },
+      {
+        path: "log",
+        name: "Log",
+        component: "views/vab/errorLog/index",
+        meta: { title: "错误日志模拟", permissions: ["admin"] },
+      },
+      {
+        path: "more",
+        name: "More",
+        component: "views/vab/more/index",
+        meta: { title: "更多组件", permissions: ["admin"] },
+      },
+    ],
+  },
+  {
+    path: "/mall",
+    component: "Layout",
+    redirect: "noRedirect",
+    name: "Mall",
+    meta: {
+      title: "商城",
+      icon: "shopping-cart",
+      permissions: ["admin"],
+    },
+
+    children: [
+      {
+        path: "pay",
+        name: "Pay",
+        component: "views/mall/pay/index",
+        meta: {
+          title: "支付",
+          noKeepAlive: true,
+        },
+        children: null,
+      },
+      {
+        path: "goodsList",
+        name: "GoodsList",
+        component: "views/mall/goodsList/index",
+        meta: {
+          title: "商品列表",
+        },
+      },
+      {
+        path: "goodsDetail",
+        name: "GoodsDetail",
+        component: "views/mall/goodsDetail/index",
+        meta: {
+          title: "商品详情",
+        },
+      },
+    ],
+  },
+  {
+    path: "/error",
+    component: "EmptyLayout",
+    redirect: "noRedirect",
+    name: "Error",
+    meta: { title: "错误页", icon: "bug" },
+    children: [
+      {
+        path: "401",
+        name: "Error401",
+        component: "views/401",
+        meta: { title: "401" },
+      },
+      {
+        path: "404",
+        name: "Error404",
+        component: "views/404",
+        meta: { title: "404" },
+      },
+    ],
+  },
+];
+export default [
+  {
+    url: "/menu/navigate",
+    type: "post",
+    response: () => {
+      return { code: 200, msg: "success", data: data };
+    },
+  },
+];

+ 88 - 0
mock/controller/table.js

@@ -0,0 +1,88 @@
+import { mock } from "mockjs";
+import { handleRandomImage } from "../utils";
+const List = [];
+const count = 999;
+for (let i = 0; i < count; i++) {
+  List.push(
+    mock({
+      uuid: "@uuid",
+      id: "@id",
+      title: "@csentence(1, 2)",
+      "status|1": ["published", "draft", "deleted"],
+      author: "@cname",
+      datetime: "@datetime",
+      pageViews: "@integer(300, 5000)",
+      img: handleRandomImage(200, 200),
+      smallImg: handleRandomImage(40, 40),
+      switch: "@boolean",
+      percent: "@integer(80,99)",
+    })
+  );
+}
+
+export default [
+  {
+    url: "/table/getList",
+    type: "post",
+    response: (config) => {
+      if (!config.body) {
+        return {
+          code: 200,
+          msg: "success",
+          totalCount: count,
+          data: mock({
+            "data|50": [
+              {
+                id: "@id",
+                title: "@csentence(1, 2)",
+                "status|1": ["published", "draft", "deleted"],
+                author: "@cname",
+                datetime: "@datetime",
+                pageViews: "@integer(300, 5000)",
+                img: handleRandomImage(200, 200),
+                smallImg: handleRandomImage(40, 40),
+                switch: "@boolean",
+                percent: "@integer(80,99)",
+              },
+            ],
+          }).data,
+        };
+      }
+      const { title = "", pageNo = 1, pageSize = 20 } = config.body;
+      let mockList = List.filter((item) => {
+        if (title && item.title.indexOf(title) < 0) return false;
+        return true;
+      });
+      const pageList = mockList.filter(
+        (item, index) =>
+          index < pageSize * pageNo && index >= pageSize * (pageNo - 1)
+      );
+      return {
+        code: 200,
+        msg: "success",
+        totalCount: count,
+        data: pageList,
+      };
+    },
+  },
+  {
+    url: "/table/doEdit",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟保存成功",
+      };
+    },
+  },
+  {
+    url: "/table/doDelete",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "模拟删除成功",
+      };
+    },
+  },
+];

+ 54 - 0
mock/controller/tree.js

@@ -0,0 +1,54 @@
+const data = [
+  {
+    id: "1",
+    parentId: "0",
+    name: "vue-admin-beautiful科技有限公司",
+    title: "vue-admin-beautiful科技有限公司",
+    text: "vue-admin-beautiful科技有限公司",
+    value: "1",
+    rank: 1,
+    children: [
+      {
+        id: "32816b88ff72423f960e7d492a386131",
+        parentId: "1",
+        name: "1103工作室",
+        title: "1103工作室",
+        text: "1103工作室",
+        value: "32816b88ff72423f960e7d492a386131",
+        rank: 2,
+        children: [
+          {
+            id: "9e11afc35d55475fb0bd3164b9684cbe",
+            parentId: "32816b88ff72423f960e7d492a386131",
+            name: "前端牛逼plus小组",
+            title: "前端牛逼plus小组",
+            text: "前端牛逼plus小组",
+            value: "9e11afc35d55475fb0bd3164b9684cbe",
+            rank: 3,
+            children: [
+              {
+                id: "4cc1b04635e4444292526c5391699077",
+                parentId: "9e11afc35d55475fb0bd3164b9684cbe",
+                name: "组员chuzhixin",
+                title: "组员chuzhixin",
+                text: "组员chuzhixin",
+                value: "4cc1b04635e4444292526c5391699077",
+                rank: 4,
+                children: [],
+              },
+            ],
+          },
+        ],
+      },
+    ],
+  },
+];
+export default [
+  {
+    url: "/tree/list",
+    type: "post",
+    response: () => {
+      return { code: 200, msg: "success", data };
+    },
+  },
+];

+ 14 - 0
mock/controller/upload.js

@@ -0,0 +1,14 @@
+const data = [];
+export default [
+  {
+    url: "/upload",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "success",
+        data: data,
+      };
+    },
+  },
+];

+ 95 - 0
mock/controller/user.js

@@ -0,0 +1,95 @@
+import { handleRandomImage } from "../utils";
+const accessTokens = {
+  admin: "admin-accessToken",
+  editor: "editor-accessToken",
+  test: "test-accessToken",
+};
+
+export default [
+  {
+    url: "/publicKey",
+    type: "post",
+    response: (config) => {
+      return {
+        code: 200,
+        msg: "success",
+        data: {
+          mockServer: true,
+        },
+      };
+    },
+  },
+  {
+    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",
+      };
+    },
+  },
+];

+ 70 - 0
mock/controller/userManagement.js

@@ -0,0 +1,70 @@
+const totalCount = 3;
+const List = [
+  {
+    id: "@id",
+    userName: "admin",
+    password: "admin",
+    email: "@email",
+    permissions: ["admin"],
+    datatime: "@datetime",
+  },
+  {
+    id: "@id",
+    userName: "editor",
+    password: "editor",
+    email: "@email",
+    permissions: ["editor"],
+    datatime: "@datetime",
+  },
+  {
+    id: "@id",
+    userName: "test",
+    password: "test",
+    email: "@email",
+    permissions: ["admin", "editor"],
+    datatime: "@datetime",
+  },
+];
+export default [
+  {
+    url: "/userManagement/getList",
+    type: "post",
+    response: (config) => {
+      const { title = "", pageNo = 1, pageSize = 20 } = config.body;
+      let mockList = List.filter((item) => {
+        if (title && item.title.indexOf(title) < 0) return false;
+        return true;
+      });
+      const pageList = mockList.filter(
+        (item, index) =>
+          index < pageSize * pageNo && index >= pageSize * (pageNo - 1)
+      );
+      return {
+        code: 200,
+        msg: "success",
+        totalCount,
+        data: pageList,
+      };
+    },
+  },
+  {
+    url: "/userManagement/doEdit",
+    type: "post",
+    response: () => {
+      return {
+        code: 200,
+        msg: "模拟保存成功",
+      };
+    },
+  },
+  {
+    url: "/userManagement/doDelete",
+    type: "post",
+    response: () => {
+      return {
+        code: 200,
+        msg: "模拟删除成功",
+      };
+    },
+  },
+];

+ 35 - 0
mock/index.js

@@ -0,0 +1,35 @@
+/**
+ * @copyright chuzhixin 1204505056@qq.com
+ * @description 导入所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。
+ */
+
+import { handleMockArray } from "./utils";
+import chalk from "chalk";
+import fs from "fs";
+import { baseURL, devPort, httpRequestFile } from "../src/config/settings";
+
+const mocks = [];
+const mockArray = handleMockArray();
+
+if (httpRequestFile) {
+  fs.writeFile("./http/mock.http", "", {}, function (err) {
+    if (err) throw err;
+  });
+}
+mockArray.forEach(async (item) => {
+  const obj = require(item).default;
+  await mocks.push(...obj);
+  if (httpRequestFile) {
+    obj.forEach((item) => {
+      fs.appendFile(
+        "./http/mock.http",
+        `\r\n###${item.url}###\r\POST http://localhost:${devPort}/${baseURL}${item.url}\r\nContent-Type: application/x-www-form-urlencoded\r\n###\r\n`,
+        (error) => {
+          if (error)
+            return chalk.red(`\n > 追加HTTP Request失败${error.message}`);
+        }
+      );
+    });
+  }
+});
+export default mocks;

+ 94 - 0
mock/mockServer.js

@@ -0,0 +1,94 @@
+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/settings");
+const mockDir = path.join(process.cwd(), "mock");
+
+/**
+ *
+ * @param app
+ * @returns {{mockStartIndex: number, mockRoutesLength: number}}
+ */
+function registerRoutes(app) {
+  let mockLastIndex;
+  const { default: 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) {
+      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) => {
+  require("@babel/register");
+  app.use(bodyParser.json());
+  app.use(
+    bodyParser.urlencoded({
+      extended: true,
+    })
+  );
+
+  const mockRoutes = registerRoutes(app);
+  let mockRoutesLength = mockRoutes.mockRoutesLength;
+  let mockStartIndex = mockRoutes.mockStartIndex;
+  chokidar
+    .watch(mockDir, {
+      ignored: /mock-server/,
+      ignoreInitial: true,
+    })
+    .on("all", (event) => {
+      if (event === "change" || event === "add") {
+        try {
+          app._router.stack.splice(mockStartIndex, mockRoutesLength);
+
+          Object.keys(require.cache).forEach((item) => {
+            if (item.includes(mockDir)) {
+              delete require.cache[require.resolve(item)];
+            }
+          });
+          const mockRoutes = registerRoutes(app);
+          mockRoutesLength = mockRoutes.mockRoutesLength;
+          mockStartIndex = mockRoutes.mockStartIndex;
+        } catch (error) {
+          console.log(chalk.red(error));
+        }
+      }
+    });
+};

+ 53 - 0
mock/static.js

@@ -0,0 +1,53 @@
+/**
+ * @copyright chuzhixin 1204505056@qq.com
+ * @description 导入所有 controller 模块,浏览器环境中自动输出controller文件夹下Mock接口,请勿修改。
+ */
+import Mock from "mockjs";
+import { paramObj } from "../src/utils";
+
+const mocks = [];
+const files = require.context("./controller", false, /\.js$/);
+
+files.keys().forEach((key) => {
+  const obj = files(key).default;
+  mocks.push(...obj);
+});
+
+export function mockXHR() {
+  Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send;
+  Mock.XHR.prototype.send = function () {
+    if (this.custom.xhr) {
+      this.custom.xhr.withCredentials = this.withCredentials || false;
+
+      if (this.responseType) {
+        this.custom.xhr.responseType = this.responseType;
+      }
+    }
+    this.proxy_send(...arguments);
+  };
+
+  function XHR2ExpressReqWrap(respond) {
+    return function (options) {
+      let result = null;
+      if (respond instanceof Function) {
+        const { body, type, url } = options;
+        result = respond({
+          method: type,
+          body: JSON.parse(body),
+          query: paramObj(url),
+        });
+      } else {
+        result = respond;
+      }
+      return Mock.mock(result);
+    };
+  }
+
+  for (const i of mocks) {
+    Mock.mock(
+      new RegExp(i.url),
+      i.type || "get",
+      XHR2ExpressReqWrap(i.response)
+    );
+  }
+}

+ 39 - 0
mock/utils/index.js

@@ -0,0 +1,39 @@
+import { Random } from "mockjs";
+import { join } from "path";
+import fs from "fs";
+
+/**
+ * @copyright chuzhixin 1204505056@qq.com
+ * @description 随机生成图片url。
+ * @param width
+ * @param height
+ * @returns {string}
+ */
+export function handleRandomImage(width = 50, height = 50) {
+  return `https://picsum.photos/${width}/${height}?random=${Random.guid()}`;
+}
+
+/**
+ * @copyright chuzhixin 1204505056@qq.com
+ * @description 处理所有 controller 模块,npm run serve时在node环境中自动输出controller文件夹下Mock接口,请勿修改。
+ * @returns {[]}
+ */
+export 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;
+}

+ 121 - 0
package.json

@@ -0,0 +1,121 @@
+{
+  "name": "vue-admin-beautiful",
+  "version": "1.0.0",
+  "private": true,
+  "author": "chuzhixin",
+  "participants": [],
+  "homepage": "https://chu1204505056.gitee.io/vue-admin-beautiful",
+  "scripts": {
+    "serve": "npm run helper&&vue-cli-service serve",
+    "build": "npm run helper&&vue-cli-service build",
+    "build:preview": "npm run helper&&vue-cli-service build --mode preview",
+    "globle": "npm install -g cnpm --registry=https://registry.npm.taobao.org&&cnpm i rimraf npm-check-updates nrm -g&&rimraf node_modules&&cnpm i",
+    "lint": "vue-cli-service lint --fix",
+    "lint:style": "stylelint **/*.{vue,css,scss} --fix",
+    "inspect": "vue-cli-service inspect",
+    "template": "plop",
+    "clear": "rimraf node_modules&&cnpm i&&increase-memory-limit",
+    "use:npm": "nrm use npm",
+    "use:taobao": "nrm use taobao",
+    "update": "ncu -u --concurrency 10 --timeout 80000&&cnpm i",
+    "update:globle": "ncu -g --concurrency 10 --timeout 80000",
+    "svgo": "svgo -f src/remixIcon/svg --config=svgo.yml",
+    "push": "start ./push.sh",
+    "deploy": "start ./deploy.sh",
+    "increase-memory-limit": "increase-memory-limit",
+    "helper": "node node_modules/zx-layouts"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/chuzhixin/vue-admin-beautiful.git"
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged"
+    }
+  },
+  "lint-staged": {
+    "src/**/*.{js,vue}": [
+      "eslint --fix",
+      "git add"
+    ]
+  },
+  "dependencies": {
+    "axios": "^0.19.2",
+    "better-scroll": "^1.15.2",
+    "clipboard": "^2.0.6",
+    "codemirror": "^5.55.0",
+    "core-js": "^3.6.5",
+    "dayjs": "^1.8.29",
+    "echarts": "^4.8.0",
+    "echarts-wordcloud": "^1.1.3",
+    "element-ui": "^2.13.2",
+    "file-saver": "^2.0.2",
+    "js-cookie": "^2.2.1",
+    "jsencrypt": "^3.0.0-rc.1",
+    "jsonlint": "^1.6.3",
+    "lodash": "^4.17.19",
+    "maptalks": "^0.47.5",
+    "mapv": "^2.0.56",
+    "nprogress": "^0.2.0",
+    "qs": "^6.9.4",
+    "screenfull": "^5.0.2",
+    "vue": "^2.6.11",
+    "vue-amap": "^0.5.10",
+    "vue-echarts": "^5.0.0-beta.0",
+    "vue-qart": "^2.2.0",
+    "vue-router": "^3.3.4",
+    "vuedraggable": "^2.24.0",
+    "vuex": "^3.5.1",
+    "xlsx": "^0.16.3",
+    "zx-comparison": "^1.0.3",
+    "zx-count": "^0.3.7",
+    "zx-icon": "^1.1.1",
+    "zx-keel": "^0.9.4",
+    "zx-layouts": "^0.5.7",
+    "zx-magnifie": "^0.4.0",
+    "zx-markdown-editor": "^0.0.2",
+    "zx-player": "^0.9.6",
+    "zx-quill": "^0.0.2",
+    "zx-templates": "^0.0.10",
+    "zx-verify": "^0.0.2"
+  },
+  "devDependencies": {
+    "@babel/register": "^7.10.4",
+    "@vue/cli-plugin-babel": "^4.4.6",
+    "@vue/cli-plugin-eslint": "^4.4.6",
+    "@vue/cli-plugin-router": "^4.4.6",
+    "@vue/cli-plugin-vuex": "^4.4.6",
+    "@vue/cli-service": "^4.4.6",
+    "@vue/eslint-config-prettier": "^6.0.0",
+    "autoprefixer": "^9.8.5",
+    "babel-eslint": "^10.1.0",
+    "compression-webpack-plugin": "^4.0.0",
+    "eslint": "^7.4.0",
+    "eslint-plugin-prettier": "^3.1.4",
+    "eslint-plugin-vue": "^6.2.2",
+    "filemanager-webpack-plugin": "^2.0.5",
+    "husky": "^4.2.5",
+    "image-webpack-loader": "^6.0.0",
+    "increase-memory-limit": "^1.0.7",
+    "lint-staged": "^10.2.11",
+    "mockjs": "^1.1.0",
+    "plop": "^2.7.1",
+    "prettier": "^2.0.5",
+    "sass": "^1.26.10",
+    "sass-loader": "^9.0.2",
+    "script-loader": "^0.7.2",
+    "stylelint": "^13.6.1",
+    "stylelint-config-recess-order": "^2.0.4",
+    "stylelint-config-standard": "^20.0.0",
+    "stylelint-order": "^4.1.0",
+    "svg-sprite-loader": "^5.0.0",
+    "svgo": "^1.3.2",
+    "vue-template-compiler": "^2.6.11",
+    "webpackbar": "^4.0.0"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  }
+}

+ 13 - 0
plopfile.js

@@ -0,0 +1,13 @@
+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);
+};

+ 5 - 0
postcss.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: {
+    autoprefixer: {},
+  },
+};

+ 15 - 0
prettier.config.js

@@ -0,0 +1,15 @@
+module.exports = {
+  printWidth: 80,
+  tabWidth: 2,
+  useTabs: false,
+  semi: true,
+  singleQuote: false,
+  quoteProps: "as-needed",
+  jsxSingleQuote: false,
+  trailingComma: "es5",
+  bracketSpacing: true,
+  jsxBracketSameLine: false,
+  arrowParens: "always",
+  vueIndentScriptAndStyle: false,
+  endOfLine: "lf",
+};

BIN
public/favicon.ico


BIN
public/favicon_backup.ico


+ 47 - 0
public/index.html

@@ -0,0 +1,47 @@
+<!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-beautiful,vue-admin-beautiful官网,vue-admin-beautiful文档,vue-element-admin,vue-element-admin官网,vue-element-admin文档,vue-admin,vue-admin官网,vue-admin文档"
+    />
+    <meta
+      name="description"
+      content="<%= VUE_APP_TITLE %>官网与文档基于vue-admin-beautiful构建,简称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-beautiful开发
+    </noscript>
+    <div id="vue-admin-beautiful">
+      <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-beautiful开发的项目需要部署到服务器下访问");
+    </script>
+  </body>
+</html>

+ 96 - 0
public/static/css/loading.css

@@ -0,0 +1,96 @@
+.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;
+  }
+}

+ 13 - 0
push.sh

@@ -0,0 +1,13 @@
+#!/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-beautiful.git" master
+git push -f "https://${access_token}@gitee.com/chu1204505056/vue-admin-beautiful.git" master
+start "https://github.com/chuzhixin/vue-admin-beautiful"
+exec /bin/bash
+
+
+
+

+ 12 - 0
src/App.vue

@@ -0,0 +1,12 @@
+<template>
+  <div id="vue-admin-beautiful">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+  name: "App",
+  mounted() {},
+};
+</script>

+ 9 - 0
src/api/ad.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/ad/getList",
+    method: "get",
+    data,
+  });
+}

+ 9 - 0
src/api/changeLog.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/changeLog/getList",
+    method: "post",
+    data,
+  });
+}

+ 9 - 0
src/api/colorfulIcon.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getIconList(data) {
+  return request({
+    url: "/colorfulIcon/getList",
+    method: "post",
+    data,
+  });
+}

+ 25 - 0
src/api/face.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/face/list",
+    method: "post",
+    data,
+  });
+}
+
+export function doEdit(data) {
+  return request({
+    url: "/face/edit",
+    method: "post",
+    data,
+  });
+}
+
+export function doDelete(data) {
+  return request({
+    url: "/face/delete",
+    method: "post",
+    data,
+  });
+}

+ 20 - 0
src/api/github.js

@@ -0,0 +1,20 @@
+import request from "axios";
+
+export function getRepos(params) {
+  return request({
+    url: "https://api.github.com/repos/chuzhixin/vue-admin-beautiful",
+    method: "get",
+    params,
+    timeout: 10000,
+  });
+}
+
+export function getStargazers(params) {
+  return request({
+    url:
+      "https://api.github.com/repos/chuzhixin/vue-admin-beautiful/stargazers",
+    method: "get",
+    params,
+    timeout: 10000,
+  });
+}

+ 9 - 0
src/api/goodsDetail.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/goodsDetail/getList",
+    method: "post",
+    data,
+  });
+}

+ 9 - 0
src/api/goodsList.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/goodsList/getList",
+    method: "post",
+    data,
+  });
+}

+ 9 - 0
src/api/icon.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getIconList(data) {
+  return request({
+    url: "/icon/getList",
+    method: "post",
+    data,
+  });
+}

+ 25 - 0
src/api/menuManagement.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function getTree(data) {
+  return request({
+    url: "/menuManagement/getTree",
+    method: "post",
+    data,
+  });
+}
+
+export function doEdit(data) {
+  return request({
+    url: "/menuManagement/doEdit",
+    method: "post",
+    data,
+  });
+}
+
+export function doDelete(data) {
+  return request({
+    url: "/menuManagement/doDelete",
+    method: "post",
+    data,
+  });
+}

+ 8 - 0
src/api/notice.js

@@ -0,0 +1,8 @@
+import request from "@/utils/request";
+
+export function getNoticeList() {
+  return request({
+    url: "/notice/getList",
+    method: "post",
+  });
+}

+ 25 - 0
src/api/personalCenter.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/personalCenter/getList",
+    method: "post",
+    data,
+  });
+}
+
+export function doEdit(data) {
+  return request({
+    url: "/personalCenter/doEdit",
+    method: "post",
+    data,
+  });
+}
+
+export function doDelete(data) {
+  return request({
+    url: "/personalCenter/doDelete",
+    method: "post",
+    data,
+  });
+}

+ 8 - 0
src/api/publicKey.js

@@ -0,0 +1,8 @@
+import request from "@/utils/request";
+
+export function getPublicKey(data) {
+  return request({
+    url: "/publicKey",
+    method: "post",
+  });
+}

+ 9 - 0
src/api/remixIcon.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getIconList(data) {
+  return request({
+    url: "/remixIcon/getList",
+    method: "post",
+    data,
+  });
+}

+ 25 - 0
src/api/roleManagement.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/roleManagement/getList",
+    method: "post",
+    data,
+  });
+}
+
+export function doEdit(data) {
+  return request({
+    url: "/roleManagement/doEdit",
+    method: "post",
+    data,
+  });
+}
+
+export function doDelete(data) {
+  return request({
+    url: "/roleManagement/doDelete",
+    method: "post",
+    data,
+  });
+}

+ 9 - 0
src/api/router.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getRouterList(data) {
+  return request({
+    url: "/menu/navigate",
+    method: "post",
+    data,
+  });
+}

+ 25 - 0
src/api/table.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/table/getList",
+    method: "post",
+    data,
+  });
+}
+
+export function doEdit(data) {
+  return request({
+    url: "/table/doEdit",
+    method: "post",
+    data,
+  });
+}
+
+export function doDelete(data) {
+  return request({
+    url: "/table/doDelete",
+    method: "post",
+    data,
+  });
+}

+ 9 - 0
src/api/tree.js

@@ -0,0 +1,9 @@
+import request from "@/utils/request";
+
+export function getTreeList(data) {
+  return request({
+    url: "/tree/list",
+    method: "post",
+    data,
+  });
+}

+ 37 - 0
src/api/user.js

@@ -0,0 +1,37 @@
+import request from "@/utils/request";
+import { encryptedData } from "@/utils/encrypt";
+import { loginRSA } from "@/config/settings";
+
+export async function login(data) {
+  if (loginRSA) {
+    data = await encryptedData(data);
+  }
+  return request({
+    url: "/login",
+    method: "post",
+    data,
+  });
+}
+
+export function getInfo(accessToken) {
+  return request({
+    url: "/userInfo",
+    method: "post",
+    data: {
+      accessToken,
+    },
+  });
+}
+
+export function logout() {
+  return request({
+    url: "/logout",
+    method: "post",
+  });
+}
+export function register() {
+  return request({
+    url: "/register",
+    method: "post",
+  });
+}

+ 25 - 0
src/api/userManagement.js

@@ -0,0 +1,25 @@
+import request from "@/utils/request";
+
+export function getList(data) {
+  return request({
+    url: "/userManagement/getList",
+    method: "post",
+    data,
+  });
+}
+
+export function doEdit(data) {
+  return request({
+    url: "/userManagement/doEdit",
+    method: "post",
+    data,
+  });
+}
+
+export function doDelete(data) {
+  return request({
+    url: "/userManagement/doDelete",
+    method: "post",
+    data,
+  });
+}

BIN
src/assets/comparison/left.jpg


BIN
src/assets/comparison/right.jpg


BIN
src/assets/error_images/401.png


BIN
src/assets/error_images/404.png


BIN
src/assets/error_images/cloud.png


BIN
src/assets/ewm.png


BIN
src/assets/login_images/background.jpg


BIN
src/assets/qr_logo/lqr_logo.png


+ 17 - 0
src/colorfulIcon/index.js

@@ -0,0 +1,17 @@
+import Vue from "vue";
+import ColorfullIcon from "@/components/ColorfullIcon";
+
+Vue.component("vab-colorful-icon", ColorfullIcon);
+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);

+ 1 - 0
src/colorfulIcon/svg/alphabetical_sorting.svg

@@ -0,0 +1 @@
+<svg class="icon" width="128" height="128" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg"><path d="M358.4 853.333H245.333l-23.466 64H147.2l121.6-324.266h61.867l119.466 324.266h-68.266l-23.467-64zm-98.133-57.6h81.066l-40.533-121.6-40.533 121.6zm4.266-418.133h162.134v53.333H179.2V390.4L341.333 160H179.2v-53.333h243.2v36.266L264.533 377.6z" fill="#2196F3"/><path d="M810.667 704V106.667h-85.334V704h-128L768 917.333 938.667 704z" fill="#546E7A"/></svg>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 22 - 0
src/colorfulIcon/svg/vab.svg


+ 65 - 0
src/components/ColorfullIcon/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <img
+    v-if="isExternal"
+    :src="styleExternalIcon"
+    class="svg-external-icon svg-icon"
+    v-on="$listeners"
+  />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :xlink:href="iconName" />
+  </svg>
+</template>
+
+<script>
+import { isExternal } from "@/utils/validate";
+
+export default {
+  name: "ColorfulIcon",
+  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>

+ 111 - 0
src/components/JsonEditor/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="json-editor">
+    <label>
+      <textarea ref="textarea" />
+    </label>
+  </div>
+</template>
+
+<script>
+import CodeMirror from "codemirror";
+import "codemirror/addon/lint/lint.css";
+import "codemirror/lib/codemirror.css";
+import "codemirror/theme/rubyblue.css";
+import "codemirror/mode/javascript/javascript";
+import "codemirror/addon/lint/lint";
+import "codemirror/addon/lint/json-lint";
+
+require("script-loader!jsonlint");
+
+export default {
+  name: "JsonEditor",
+  props: {
+    value: {
+      type: [Array, Object],
+      default: () => {
+        return null;
+      },
+    },
+  },
+  data() {
+    return {
+      jsonEditor: false,
+    };
+  },
+  watch: {
+    value(value) {
+      const editorValue = this.jsonEditor.getValue();
+
+      if (editorValue) {
+        this.$emit("change", editorValue);
+      } else {
+        this.$baseMessage("JSON不能为空,否则无法生成表格", "error");
+      }
+      if (value !== editorValue) {
+        this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
+      }
+    },
+  },
+  mounted() {
+    this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
+      lineNumbers: true,
+      mode: "application/json",
+      gutters: ["CodeMirror-lint-markers"],
+      theme: "rubyblue",
+      lint: true,
+    });
+
+    this.jsonEditor.setValue(JSON.stringify(this.value, null, 2));
+    this.jsonEditor.on("change", (cm) => {
+      if (this.isJsonString(cm.getValue())) {
+        this.$emit("change", cm.getValue());
+      }
+    });
+  },
+  methods: {
+    getValue() {
+      return this.jsonEditor.getValue();
+    },
+    isJsonString(str) {
+      try {
+        if (typeof JSON.parse(str) == "object") {
+          return true;
+        }
+      } catch (e) {}
+      return false;
+    },
+  },
+};
+</script>
+
+<style scoped>
+.json-editor {
+  position: relative;
+  height: 100%;
+}
+
+.json-editor >>> .CodeMirror {
+  height: auto;
+  min-height: calc(100vh - 220px);
+}
+
+.json-editor >>> .CodeMirror-scroll {
+  min-height: calc(100vh - 220px);
+}
+
+.json-editor >>> .cm-s-rubyblue span.cm-string {
+  color: #f08047;
+}
+
+.json-editor >>> .cm-s-rubyblue .CodeMirror-gutters {
+  padding-right: 10px;
+
+  /* background: transparent; */
+  border-right: 1px solid #fff;
+}
+
+.json-editor >>> .cm-s-rubyblue.CodeMirror {
+  /* background: #08233e; */
+  color: white;
+}
+</style>

+ 69 - 0
src/components/RemixIcon/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div
+    v-if="isExternal"
+    :style="styleExternalIcon"
+    class="svg-external-icon svg-icon"
+    v-on="$listeners"
+  />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :xlink:href="iconName" />
+  </svg>
+</template>
+
+<script>
+import { isExternal } from "@/utils/validate";
+
+export default {
+  name: "RemixIcon",
+  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>

+ 201 - 0
src/components/SelectTree/index.vue

@@ -0,0 +1,201 @@
+<template>
+  <div class="select-tree-template">
+    <el-select
+      v-model="selectValue"
+      :clearable="clearable"
+      :collapse-tags="selectType == 'multiple'"
+      :multiple="selectType == 'multiple'"
+      class="vab-tree-select"
+      value-key="id"
+      @clear="clearHandle"
+      @remove-tag="removeTag"
+    >
+      <el-option :value="selectKey">
+        <el-tree
+          id="treeOption"
+          ref="treeOption"
+          :current-node-key="currentNodeKey"
+          :data="treeOptions"
+          :default-checked-keys="defaultSelectedKeys"
+          :default-expanded-keys="defaultSelectedKeys"
+          :highlight-current="true"
+          :props="defaultProps"
+          :show-checkbox="selectType == 'multiple'"
+          node-key="id"
+          @check="checkNode"
+          @node-click="nodeClick"
+        ></el-tree>
+      </el-option>
+    </el-select>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "SelectTreeTemplate",
+  props: {
+    /* 树形结构数据 */
+    treeOptions: {
+      type: Array,
+      default: () => {
+        return [];
+      },
+    },
+    /* 单选/多选 */
+    selectType: {
+      type: String,
+      default: () => {
+        return "single";
+      },
+    },
+    /* 初始选中值key */
+    selectedKey: {
+      type: String,
+      default: () => {
+        return "";
+      },
+    },
+    /* 初始选中值name */
+    selectedValue: {
+      type: String,
+      default: () => {
+        return "";
+      },
+    },
+    /* 可做选择的层级 */
+    selectLevel: {
+      type: [String, Number],
+      default: () => {
+        return "";
+      },
+    },
+    /* 可清空选项 */
+    clearable: {
+      type: Boolean,
+      default: () => {
+        return true;
+      },
+    },
+  },
+  data() {
+    return {
+      defaultProps: {
+        children: "children",
+        label: "name",
+      },
+      defaultSelectedKeys: [], //初始选中值数组
+      currentNodeKey: this.selectedKey,
+      selectValue:
+        this.selectType == "multiple"
+          ? this.selectedValue.split(",")
+          : this.selectedValue, //下拉框选中值label
+      selectKey:
+        this.selectType == "multiple"
+          ? this.selectedKey.split(",")
+          : this.selectedKey, //下拉框选中值value
+    };
+  },
+  mounted() {
+    const that = this;
+    this.initTree();
+  },
+  methods: {
+    // 初始化树的值
+    initTree() {
+      const that = this;
+      if (that.selectedKey) {
+        that.defaultSelectedKeys = that.selectedKey.split(","); // 设置默认展开
+        if (that.selectType == "single") {
+          that.$refs.treeOption.setCurrentKey(that.selectedKey); // 设置默认选中
+        } else {
+          that.$refs.treeOption.setCheckedKeys(that.defaultSelectedKeys);
+        }
+      }
+    },
+    // 清除选中
+    clearHandle() {
+      const that = this;
+      this.selectValue = "";
+      this.selectKey = "";
+      this.defaultSelectedKeys = [];
+      this.currentNodeKey = "";
+      this.clearSelected();
+      if (that.selectType == "single") {
+        that.$refs.treeOption.setCurrentKey(""); // 设置默认选中
+      } else {
+        that.$refs.treeOption.setCheckedKeys([]);
+      }
+    },
+    /* 清空选中样式 */
+    clearSelected() {
+      const allNode = document.querySelectorAll("#treeOption .el-tree-node");
+      allNode.forEach((element) => element.classList.remove("is-current"));
+    },
+    // select多选时移除某项操作
+    removeTag(val) {
+      this.$refs.treeOption.setCheckedKeys([]);
+    },
+    // 点击叶子节点
+    nodeClick(data, node, el) {
+      if (data.rank >= this.selectLevel) {
+        this.selectValue = data.name;
+        this.selectKey = data.id;
+      }
+    },
+    // 节点选中操作
+    checkNode(data, node, el) {
+      const checkedNodes = this.$refs.treeOption.getCheckedNodes();
+      const keyArr = [];
+      const valueArr = [];
+      checkedNodes.forEach((item) => {
+        if (item.rank >= this.selectLevel) {
+          keyArr.push(item.id);
+          valueArr.push(item.name);
+        }
+      });
+      this.selectValue = valueArr;
+      this.selectKey = keyArr;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
+  height: auto;
+  max-height: 274px;
+  padding: 0;
+  overflow-y: auto;
+}
+
+.el-select-dropdown__item.selected {
+  font-weight: normal;
+}
+
+ul li > .el-tree .el-tree-node__content {
+  height: auto;
+  padding: 0 20px;
+}
+
+.el-tree-node__label {
+  font-weight: normal;
+}
+
+.el-tree > .is-current .el-tree-node__label {
+  font-weight: 700;
+  color: #409eff;
+}
+
+.el-tree > .is-current .el-tree-node__children .el-tree-node__label {
+  font-weight: normal;
+  color: #606266;
+}
+</style>
+<style lang="scss">
+/* .vab-tree-select{
+      .el-tag__close.el-icon-close{
+        width:0;
+        overflow:hidden;
+      }
+    } */
+</style>

+ 160 - 0
src/components/UploadExcel/index.vue

@@ -0,0 +1,160 @@
+<template>
+  <div>
+    <input
+      ref="excel-upload-input"
+      class="excel-upload-input"
+      type="file"
+      accept=".xlsx, .xls"
+      @change="handleClick"
+    />
+    <div
+      class="drop"
+      @drop="handleDrop"
+      @dragover="handleDragover"
+      @dragenter="handleDragover"
+    >
+      将excel文件拖拽到此处或
+
+      <el-button
+        :loading="loading"
+        size="mini"
+        type="primary"
+        @click="handleUpload"
+      >
+        点击上传
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+import XLSX from "xlsx";
+
+export default {
+  props: {
+    beforeUpload: {
+      type: Function,
+      default: () => {},
+    },
+    onSuccess: {
+      type: Function,
+      default: () => {},
+    },
+  },
+  data() {
+    return {
+      loading: false,
+      excelData: {
+        header: null,
+        results: null,
+      },
+    };
+  },
+  methods: {
+    generateData({ header, results }) {
+      this.excelData.header = header;
+      this.excelData.results = results;
+      this.onSuccess && this.onSuccess(this.excelData);
+    },
+    handleDrop(e) {
+      e.stopPropagation();
+      e.preventDefault();
+      if (this.loading) return;
+      const files = e.dataTransfer.files;
+      if (files.length !== 1) {
+        this.$message.error("只支持上传一个文件!");
+        return;
+      }
+      const rawFile = files[0];
+
+      if (!this.isExcel(rawFile)) {
+        this.$message.error("仅支持上载.xlsx、.xls、.csv后缀文件!");
+        return false;
+      }
+      this.upload(rawFile);
+      e.stopPropagation();
+      e.preventDefault();
+    },
+    handleDragover(e) {
+      e.stopPropagation();
+      e.preventDefault();
+      e.dataTransfer.dropEffect = "复制";
+    },
+    handleUpload() {
+      this.$refs["excel-upload-input"].click();
+    },
+    handleClick(e) {
+      const files = e.target.files;
+      const rawFile = files[0];
+      if (!rawFile) return;
+      this.upload(rawFile);
+    },
+    upload(rawFile) {
+      this.$refs["excel-upload-input"].value = null;
+
+      if (!this.beforeUpload) {
+        this.readerData(rawFile);
+        return;
+      }
+      const before = this.beforeUpload(rawFile);
+      if (before) {
+        this.readerData(rawFile);
+      }
+    },
+    readerData(rawFile) {
+      this.loading = true;
+      return new Promise((resolve, reject) => {
+        const reader = new FileReader();
+        reader.onload = (e) => {
+          const data = e.target.result;
+          const workbook = XLSX.read(data, { type: "array" });
+          const firstSheetName = workbook.SheetNames[0];
+          const worksheet = workbook.Sheets[firstSheetName];
+          const header = this.getHeaderRow(worksheet);
+          const results = XLSX.utils.sheet_to_json(worksheet);
+          this.generateData({ header, results });
+          this.loading = false;
+          resolve();
+        };
+        reader.readAsArrayBuffer(rawFile);
+      });
+    },
+    getHeaderRow(sheet) {
+      const headers = [];
+      const range = XLSX.utils.decode_range(sheet["!ref"]);
+      let C;
+      const R = range.s.r;
+      for (C = range.s.c; C <= range.e.c; ++C) {
+        const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })];
+        let hdr = "UNKNOWN " + C;
+        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell);
+        headers.push(hdr);
+      }
+      return headers;
+    },
+    isExcel(file) {
+      return /\.(xlsx|xls|csv)$/.test(file.name);
+    },
+  },
+};
+</script>
+
+<style scoped>
+.excel-upload-input {
+  z-index: -9999;
+  display: none;
+}
+
+.drop {
+  position: relative;
+  width: 600px;
+  height: 160px;
+  margin: 0 auto;
+  font-size: 24px;
+  line-height: 160px;
+  color: #bbb;
+  text-align: center;
+  border: 2px dashed #bbb;
+  border-radius: 5px;
+}
+</style>

+ 191 - 0
src/components/VabCharge/index.vue

@@ -0,0 +1,191 @@
+<template>
+  <div class="content">
+    <div class="g-container" :style="styleObj">
+      <div class="g-number">
+        <vab-count
+          :start-val="startVal"
+          :end-val="endVal"
+          :duration="duration"
+          :separator="separator"
+          :prefix="prefix"
+          :suffix="suffix"
+          :decimals="decimals"
+        />
+      </div>
+      <div class="g-contrast">
+        <div class="g-circle"></div>
+        <ul class="g-bubbles">
+          <li v-for="(item, index) in 15" :key="index"></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "VabCharge",
+  props: {
+    styleObj: {
+      type: Object,
+      default: () => {
+        return {};
+      },
+    },
+    startVal: {
+      type: Number,
+      default: 0,
+    },
+    endVal: {
+      type: Number,
+      default: 100,
+    },
+  },
+  data() {
+    return {
+      decimals: 2,
+      prefix: "",
+      suffix: "%",
+      separator: ",",
+      duration: 3000,
+    };
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  position: relative;
+  display: flex;
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
+  width: 100%;
+  background: #000;
+
+  .g-number {
+    position: absolute;
+    top: 27%;
+    z-index: 99;
+    width: 300px;
+    font-size: 32px;
+    color: #fff;
+    text-align: center;
+  }
+
+  .g-container {
+    position: relative;
+    width: 300px;
+    height: 400px;
+    margin: auto;
+  }
+
+  .g-contrast {
+    width: 300px;
+    height: 400px;
+    overflow: hidden;
+    background-color: #000;
+    filter: contrast(15) hue-rotate(0);
+    animation: hueRotate 10s infinite linear;
+  }
+
+  .g-circle {
+    position: relative;
+    box-sizing: border-box;
+    width: 300px;
+    height: 300px;
+    filter: blur(8px);
+
+    &::after {
+      position: absolute;
+      top: 40%;
+      left: 50%;
+      width: 200px;
+      height: 200px;
+      content: "";
+      background-color: #00ff6f;
+      border-radius: 42% 38% 62% 49% / 45%;
+      transform: translate(-50%, -50%) rotate(0);
+      animation: rotate 10s infinite linear;
+    }
+
+    &::before {
+      position: absolute;
+      top: 40%;
+      left: 50%;
+      z-index: 99;
+      width: 176px;
+      height: 176px;
+      content: "";
+      background-color: #000;
+      border-radius: 50%;
+      transform: translate(-50%, -50%);
+    }
+  }
+
+  .g-bubbles {
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    width: 100px;
+    height: 40px;
+    background-color: #00ff6f;
+    filter: blur(5px);
+    border-radius: 100px 100px 0 0;
+    transform: translate(-50%, 0);
+  }
+
+  li {
+    position: absolute;
+    background: #00ff6f;
+    border-radius: 50%;
+  }
+
+  @for $i from 0 through 15 {
+    li:nth-child(#{$i}) {
+      $width: 15 + random(15) + px;
+
+      top: 50%;
+      left: 15 + random(70) + px;
+      width: $width;
+      height: $width;
+      transform: translate(-50%, -50%);
+      animation: moveToTop
+        #{random(6) +
+        3}s
+        ease-in-out -#{random(5000) /
+        1000}s
+        infinite;
+    }
+  }
+
+  @keyframes rotate {
+    50% {
+      border-radius: 45% / 42% 38% 58% 49%;
+    }
+
+    100% {
+      transform: translate(-50%, -50%) rotate(720deg);
+    }
+  }
+
+  @keyframes moveToTop {
+    90% {
+      opacity: 1;
+    }
+
+    100% {
+      opacity: 0.1;
+      transform: translate(-50%, -180px);
+    }
+  }
+
+  @keyframes hueRotate {
+    100% {
+      filter: contrast(15) hue-rotate(360deg);
+    }
+  }
+}
+</style>

+ 92 - 0
src/components/VabImage/index.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="vab-image__outter">
+    <el-image
+      :src="bigSrc"
+      fit="cover"
+      style="width: 100%; height: 100%;"
+      @click="clickBig"
+    ></el-image>
+    <el-image
+      :src="smallSrc"
+      class="vab-image__outter__small"
+      fit="cover"
+      @click="clickSmall"
+    ></el-image>
+    <span class="vab-image__outter__percent">{{ percent }}%</span>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "VabImage",
+  components: {},
+  props: {
+    bigSrc: {
+      type: String,
+      default: "",
+    },
+    smallSrc: {
+      type: String,
+      default: "",
+    },
+    percent: {
+      type: Number,
+      default: 97,
+    },
+  },
+  data() {
+    return {};
+  },
+  created() {},
+  mounted() {},
+  methods: {
+    clickBig() {
+      this.$emit("clickBig");
+    },
+    clickSmall() {
+      this.$emit("clickSmall");
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.vab-image {
+  &__outter {
+    position: relative;
+    width: 100%;
+    height: 100%;
+
+    ::v-deep {
+      img {
+        border-radius: $base-border-radius;
+      }
+    }
+
+    &__small {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 80px;
+      height: 100px;
+      border-bottom: 1px solid $base-color-white;
+      border-left: 1px solid $base-color-white;
+      border-radius: $base-border-radius;
+    }
+
+    &__percent {
+      position: absolute;
+      right: 0;
+      bottom: 0;
+      display: inline-block;
+      min-width: 50px;
+      height: 25px;
+      line-height: 25px;
+      color: $base-color-white;
+      text-align: center;
+      background-color: $base-color-red;
+      border-radius: $base-border-radius;
+    }
+  }
+}
+</style>

+ 313 - 0
src/components/VabProfile/index.vue

@@ -0,0 +1,313 @@
+<template>
+  <div class="card" :style="styleObj">
+    <div class="card-borders">
+      <div class="border-top"></div>
+      <div class="border-right"></div>
+      <div class="border-bottom"></div>
+      <div class="border-left"></div>
+    </div>
+    <div class="card-content">
+      <el-image :src="avatar" class="avatar"></el-image>
+      <div class="username">{{ userName }}</div>
+      <div class="social-icons">
+        <a
+          v-for="(item, index) in iconArray"
+          :key="index"
+          class="social-icon"
+          :href="item.url"
+          target="_blank"
+        >
+          <vab-icon :icon="['fas', item.icon]" />
+        </a>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "VabProfile",
+  props: {
+    styleObj: {
+      type: Object,
+      default: () => {
+        return {};
+      },
+    },
+    userName: {
+      type: String,
+      default: "",
+    },
+    avatar: {
+      type: String,
+      default: "",
+    },
+    iconArray: {
+      type: Array,
+      default: () => {
+        return [
+          { icon: "bell", url: "" },
+          { icon: "bookmark", url: "" },
+          { icon: "cloud-sun", url: "" },
+        ];
+      },
+    },
+  },
+  data() {
+    return {};
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+.card {
+  --card-bg-color: hsl(240, 31%, 25%);
+  --card-bg-color-transparent: hsla(240, 31%, 25%, 0.7);
+
+  position: relative;
+  width: 100%;
+  height: 100%;
+
+  .card-borders {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+
+    .border-top {
+      position: absolute;
+      top: 0;
+      width: 100%;
+      height: 2px;
+      background: var(--card-bg-color);
+      transform: translateX(-100%);
+      animation: slide-in-horizontal 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)
+        forwards;
+    }
+
+    .border-right {
+      position: absolute;
+      right: 0;
+      width: 2px;
+      height: 100%;
+      background: var(--card-bg-color);
+      transform: translateY(100%);
+      animation: slide-in-vertical 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)
+        forwards;
+    }
+
+    .border-bottom {
+      position: absolute;
+      bottom: 0;
+      width: 100%;
+      height: 2px;
+      background: var(--card-bg-color);
+      transform: translateX(100%);
+      animation: slide-in-horizontal-reverse 0.8s
+        cubic-bezier(0.645, 0.045, 0.355, 1) forwards;
+    }
+
+    .border-left {
+      position: absolute;
+      top: 0;
+      width: 2px;
+      height: 100%;
+      background: var(--card-bg-color);
+      transform: translateY(-100%);
+      animation: slide-in-vertical-reverse 0.8s
+        cubic-bezier(0.645, 0.045, 0.355, 1) forwards;
+    }
+  }
+
+  .card-content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    height: 100%;
+    padding: 40px 0 40px 0;
+    background: var(--card-bg-color-transparent);
+    opacity: 0;
+    transform: scale(0.6);
+    animation: bump-in 0.5s 0.8s forwards;
+
+    .avatar {
+      width: 80px;
+      height: 80px;
+      border: 1px solid $base-color-white;
+      border-radius: 50%;
+      opacity: 0;
+      transform: scale(0.6);
+      animation: bump-in 0.5s 1s forwards;
+    }
+
+    .username {
+      position: relative;
+      margin-top: 20px;
+      margin-bottom: 20px;
+      font-size: 26px;
+      color: transparent;
+      letter-spacing: 2px;
+      animation: fill-text-white 1.2s 2s forwards;
+
+      &::before {
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        color: black;
+        content: "";
+        background: #35b9f1;
+        transform: scaleX(0);
+        transform-origin: left;
+        animation: slide-in-out 1.2s 1.2s cubic-bezier(0.75, 0, 0, 1) forwards;
+      }
+    }
+
+    .social-icons {
+      display: flex;
+
+      .social-icon {
+        position: relative;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 2.5em;
+        height: 2.5em;
+        margin: 0 15px;
+        color: white;
+        text-decoration: none;
+        border-radius: 50%;
+
+        @for $i from 1 through 3 {
+          &:nth-child(#{$i}) {
+            &::before {
+              animation-delay: 2s + 0.1s * $i;
+            }
+
+            &::after {
+              animation-delay: 2.1s + 0.1s * $i;
+            }
+
+            svg {
+              animation-delay: 2.2s + 0.1s * $i;
+            }
+          }
+        }
+
+        &::before,
+        &::after {
+          position: absolute;
+          top: 0;
+          left: 0;
+          width: 100%;
+          height: 100%;
+          content: "";
+          border-radius: inherit;
+          transform: scale(0);
+        }
+
+        &::before {
+          background: #f7f1e3;
+          animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards;
+        }
+
+        &::after {
+          background: #2c3e50;
+          animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards;
+        }
+
+        svg {
+          z-index: 99;
+          transform: scale(0);
+          animation: scale-in 0.5s cubic-bezier(0.75, 0, 0, 1) forwards;
+        }
+      }
+    }
+  }
+}
+
+@keyframes bump-in {
+  50% {
+    transform: scale(1.05);
+  }
+
+  to {
+    opacity: 1;
+    transform: scale(1);
+  }
+}
+
+@keyframes slide-in-horizontal {
+  50% {
+    transform: translateX(0);
+  }
+
+  to {
+    transform: translateX(100%);
+  }
+}
+
+@keyframes slide-in-horizontal-reverse {
+  50% {
+    transform: translateX(0);
+  }
+
+  to {
+    transform: translateX(-100%);
+  }
+}
+
+@keyframes slide-in-vertical {
+  50% {
+    transform: translateY(0);
+  }
+
+  to {
+    transform: translateY(-100%);
+  }
+}
+
+@keyframes slide-in-vertical-reverse {
+  50% {
+    transform: translateY(0);
+  }
+
+  to {
+    transform: translateY(100%);
+  }
+}
+
+@keyframes slide-in-out {
+  50% {
+    transform: scaleX(1);
+    transform-origin: left;
+  }
+
+  50.1% {
+    transform-origin: right;
+  }
+
+  100% {
+    transform: scaleX(0);
+    transform-origin: right;
+  }
+}
+
+@keyframes fill-text-white {
+  to {
+    color: white;
+  }
+}
+
+@keyframes scale-in {
+  to {
+    transform: scale(1);
+  }
+}
+</style>

+ 44 - 0
src/components/VabQrCode/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="app-container">
+    <vue-q-art :config="config"></vue-q-art>
+  </div>
+</template>
+
+<script>
+import VueQArt from "vue-qart";
+import qrImg from "@/assets/qr_logo/lqr_logo.png";
+
+export default {
+  name: "VabQrCode",
+  components: {
+    VueQArt,
+  },
+  props: {
+    imagePath: {
+      type: String,
+      default: qrImg,
+    },
+    url: {
+      type: String,
+      default: "http://www.boyunvision.com/",
+    },
+    size: {
+      type: Number,
+      default: 500,
+    },
+  },
+  data() {
+    return {
+      config: {
+        value: this.url,
+        imagePath: this.imagePath,
+        filter: "color",
+        size: this.size,
+      },
+    };
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>

+ 20 - 0
src/components/VabQueryForm/VabQueryFormBottomPanel.vue

@@ -0,0 +1,20 @@
+<template>
+  <el-col :span="24">
+    <div class="bottom-panel">
+      <slot></slot>
+    </div>
+  </el-col>
+</template>
+
+<script>
+export default {
+  name: "VabQueryFormBottomPanel",
+  props: {},
+  data() {
+    return {};
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>

+ 25 - 0
src/components/VabQueryForm/VabQueryFormLeftPanel.vue

@@ -0,0 +1,25 @@
+<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>

+ 25 - 0
src/components/VabQueryForm/VabQueryFormRightPanel.vue

@@ -0,0 +1,25 @@
+<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>

+ 20 - 0
src/components/VabQueryForm/VabQueryFormTopPanel.vue

@@ -0,0 +1,20 @@
+<template>
+  <el-col :span="24">
+    <div class="top-panel">
+      <slot></slot>
+    </div>
+  </el-col>
+</template>
+
+<script>
+export default {
+  name: "VabQueryFormTopPanel",
+  props: {},
+  data() {
+    return {};
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>

+ 63 - 0
src/components/VabQueryForm/index.vue

@@ -0,0 +1,63 @@
+<template>
+  <el-row :gutter="0" class="vab-query-form">
+    <slot></slot>
+  </el-row>
+</template>
+
+<script>
+export default {
+  name: "VabQueryForm",
+  props: {},
+  data() {
+    return {};
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+@mixin panel {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: flex-start;
+}
+
+.vab-query-form {
+  margin-bottom: 10px;
+
+  ::v-deep {
+    .top-panel {
+      @include panel;
+    }
+
+    .bottom-panel {
+      @include panel;
+
+      padding-top: 14px;
+      border-top: 1px solid #dcdfe6;
+    }
+
+    .left-panel {
+      @include panel;
+
+      > .el-button,
+      .el-form-item {
+        margin: 5px;
+      }
+    }
+
+    .right-panel {
+      @include panel;
+
+      justify-content: flex-end;
+
+      .el-form-item {
+        margin: 5px;
+      }
+    }
+  }
+}
+</style>

+ 124 - 0
src/components/VabQuill/index.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="vab-quill" :class="classes">
+    <div ref="editor" :style="styles"></div>
+  </div>
+</template>
+
+<script>
+import Quill from "quill";
+import "quill/dist/quill.core.css";
+import "quill/dist/quill.snow.css";
+import "quill/dist/quill.bubble.css";
+
+export default {
+  name: "VabQuill",
+  props: {
+    value: {
+      type: String,
+      default: "",
+    },
+    border: {
+      type: Boolean,
+      default: false,
+    },
+    height: {
+      type: Number,
+      default: null,
+    },
+    minHeight: {
+      type: Number,
+      default: null,
+    },
+  },
+  data() {
+    return {
+      Quill: null,
+      currentValue: "",
+      options: {
+        theme: "snow",
+        bounds: document.body,
+        debug: "warn",
+        modules: {
+          toolbar: [
+            ["bold", "italic", "underline", "strike"],
+            [{ header: [1, 2, 3, 4, 5, 6, false] }],
+            [{ size: ["small", false, "large", "huge"] }],
+            [{ color: [] }, { background: [] }],
+            ["blockquote", "code-block"],
+            [{ list: "ordered" }, { list: "bullet" }],
+            [{ indent: "-1" }, { indent: "+1" }],
+            [{ align: [] }],
+            [{ direction: "rtl" }],
+            ["clean"],
+            ["link", "image"],
+          ],
+        },
+        placeholder: "内容...",
+        readOnly: false,
+      },
+    };
+  },
+  computed: {
+    classes() {
+      return [
+        {
+          "vab-quill-no-border": !this.border,
+        },
+      ];
+    },
+    styles() {
+      let style = {};
+      if (this.minHeight) {
+        style.minHeight = `${this.minHeight}px`;
+      }
+      if (this.height) {
+        style.height = `${this.height}px`;
+      }
+      return style;
+    },
+  },
+  watch: {
+    value: {
+      handler(val) {
+        if (val !== this.currentValue) {
+          this.currentValue = val;
+          if (this.Quill) {
+            this.Quill.pasteHTML(this.value);
+          }
+        }
+      },
+      immediate: true,
+    },
+  },
+  mounted() {
+    this.init();
+  },
+  beforeDestroy() {
+    this.Quill = null;
+  },
+  methods: {
+    init() {
+      const editor = this.$refs.editor;
+      this.Quill = new Quill(editor, this.options);
+      this.Quill.pasteHTML(this.currentValue);
+      this.Quill.on("text-change", (delta, oldDelta, source) => {
+        const html = this.$refs.editor.children[0].innerHTML;
+        const text = this.Quill.getText();
+        const quill = this.Quill;
+        this.currentValue = html;
+        this.$emit("input", html);
+        this.$emit("on-change", { html, text, quill });
+      });
+      this.Quill.on("text-change", (delta, oldDelta, source) => {
+        this.$emit("on-text-change", delta, oldDelta, source);
+      });
+      this.Quill.on("selection-change", (range, oldRange, source) => {
+        this.$emit("on-selection-change", range, oldRange, source);
+      });
+      this.Quill.on("editor-change", (eventName, ...args) => {
+        this.$emit("on-editor-change", eventName, ...args);
+      });
+    },
+  },
+};
+</script>

+ 81 - 0
src/components/VabSnow/index.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="content" :style="styleObj">
+    <div v-for="(item, index) in 200" :key="index" class="snow"></div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "VabSnow",
+  props: {
+    styleObj: {
+      type: Object,
+      default: () => {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {};
+  },
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+.content {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  background: radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%);
+  filter: drop-shadow(0 0 10px white);
+}
+
+@function random_range($min, $max) {
+  $rand: random();
+  $random_range: $min + floor($rand * (($max - $min) + 1));
+
+  @return $random_range;
+}
+
+.snow {
+  $total: 200;
+
+  position: absolute;
+  width: 10px;
+  height: 10px;
+  background: white;
+  border-radius: 50%;
+
+  @for $i from 1 through $total {
+    $random-x: random(1000000) * 0.0001vw;
+    $random-offset: random_range(-100000, 100000) * 0.0001vw;
+    $random-x-end: $random-x + $random-offset;
+    $random-x-end-yoyo: $random-x + ($random-offset / 2);
+    $random-yoyo-time: random_range(30000, 80000) / 100000;
+    $random-yoyo-y: $random-yoyo-time * 100vh;
+    $random-scale: random(10000) * 0.0001;
+    $fall-duration: random_range(10, 30) * 1s;
+    $fall-delay: random(30) * -1s;
+
+    &:nth-child(#{$i}) {
+      opacity: random(10000) * 0.0001;
+      transform: translate($random-x, -10px) scale($random-scale);
+      animation: fall-#{$i} $fall-duration $fall-delay linear infinite;
+    }
+
+    @keyframes fall-#{$i} {
+      #{percentage($random-yoyo-time)} {
+        transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);
+      }
+
+      to {
+        transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);
+      }
+    }
+  }
+}
+</style>

+ 95 - 0
src/components/VabSticky/index.vue

@@ -0,0 +1,95 @@
+<template>
+  <div :style="{ height: height + 'px', zIndex: zIndex }">
+    <div
+      :class="className"
+      :style="{
+        top: isSticky ? stickyTop + 'px' : '',
+        zIndex: zIndex,
+        position: position,
+        width: width,
+        height: height + 'px',
+      }"
+    >
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "VabSticky",
+  props: {
+    stickyTop: {
+      type: Number,
+      default: 0,
+    },
+    zIndex: {
+      type: Number,
+      default: 1,
+    },
+    className: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      active: false,
+      position: "",
+      width: undefined,
+      height: undefined,
+      isSticky: false,
+    };
+  },
+  mounted() {
+    this.height = this.$el.getBoundingClientRect().height;
+    window.addEventListener("scroll", this.handleScroll);
+    window.addEventListener("resize", this.handleResize);
+  },
+  activated() {
+    this.handleScroll();
+  },
+  destroyed() {
+    window.removeEventListener("scroll", this.handleScroll);
+    window.removeEventListener("resize", this.handleResize);
+  },
+  methods: {
+    sticky() {
+      if (this.active) {
+        return;
+      }
+      this.position = "fixed";
+      this.active = true;
+      this.width = this.width + "px";
+      this.isSticky = true;
+    },
+    handleReset() {
+      if (!this.active) {
+        return;
+      }
+      this.reset();
+    },
+    reset() {
+      this.position = "";
+      this.width = "auto";
+      this.active = false;
+      this.isSticky = false;
+    },
+    handleScroll() {
+      const width = this.$el.getBoundingClientRect().width;
+      this.width = width || "auto";
+      const offsetTop = this.$el.getBoundingClientRect().top;
+      if (offsetTop < this.stickyTop) {
+        this.sticky();
+        return;
+      }
+      this.handleReset();
+    },
+    handleResize() {
+      if (this.isSticky) {
+        this.width = this.$el.getBoundingClientRect().width + "px";
+      }
+    },
+  },
+};
+</script>

+ 267 - 0
src/components/VabUpload/index.vue

@@ -0,0 +1,267 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible.sync="dialogFormVisible"
+    width="909px"
+    :before-close="handleClose"
+    :close-on-click-modal="false"
+  >
+    <div class="upload">
+      <el-alert
+        :closable="false"
+        :title="`支持jpg、jpeg、png格式,单次可最多选择${limit}张图片,每张不可大于${size}M,如果大于${size}M会自动为您过滤`"
+        type="info"
+      >
+      </el-alert>
+      <br />
+      <el-upload
+        ref="upload"
+        class="upload-content"
+        :name="name"
+        :data="data"
+        :action="action"
+        :headers="headers"
+        :on-change="handleChange"
+        :on-preview="handlePreview"
+        :on-remove="handleRemove"
+        :on-exceed="handleExceed"
+        :on-success="handleSuccess"
+        :on-progress="handleProgress"
+        :on-error="handleError"
+        :file-list="fileList"
+        :multiple="true"
+        :auto-upload="false"
+        accept="image/png, image/jpeg"
+        :limit="limit"
+        list-type="picture-card"
+        :close-on-click-modal="false"
+      >
+        <i slot="trigger" class="el-icon-plus"></i>
+        <el-dialog
+          title="查看大图"
+          append-to-body
+          :visible.sync="dialogVisible"
+        >
+          <div style="padding-bottom: 20px !important;">
+            <img width="100%" :src="dialogImageUrl" alt="" />
+          </div>
+        </el-dialog>
+      </el-upload>
+    </div>
+    <div
+      slot="footer"
+      class="dialog-footer"
+      style="position: relative; padding-right: 15px; text-align: right;"
+    >
+      <div
+        v-if="show"
+        style="position: absolute; top: 10px; left: 15px; color: #999;"
+      >
+        正在上传中... 当前上传成功数:{{ imgSuccessNum }}张 当前上传失败数:{{
+          imgErrorNum
+        }}张
+      </div>
+      <el-button type="primary" @click="handleClose">关闭</el-button>
+      <el-button
+        style="margin-left: 10px;"
+        size="small"
+        type="success"
+        :loading="loading"
+        @click="submitUpload"
+        >开始上传
+      </el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { tokenName } from "@/config/settings";
+
+export default {
+  name: "VabUpload",
+  props: {
+    url: {
+      type: String,
+      default: "/upload",
+      required: true,
+    },
+    name: {
+      type: String,
+      default: "file",
+      required: true,
+    },
+    limit: {
+      type: Number,
+      default: 50,
+      required: true,
+    },
+    size: {
+      type: Number,
+      default: 1,
+      required: true,
+    },
+  },
+  data() {
+    return {
+      show: false,
+      loading: false,
+      dialogVisible: false,
+      dialogImageUrl: "",
+      action: "",
+      headers: {},
+      fileList: [],
+      picture: "picture",
+      imgNum: 0,
+      imgSuccessNum: 0,
+      imgErrorNum: 0,
+      typeList: null,
+      title: "上传",
+      dialogFormVisible: false,
+      data: {},
+    };
+  },
+  computed: {
+    percentage() {
+      if (this.allImgNum == 0) return 0;
+      return this.$baseLodash.round(this.imgNum / this.allImgNum, 2) * 100;
+    },
+  },
+  created() {
+    if ("development" === process.env.NODE_ENV) {
+      this.api = process.env.VUE_APP_BASE_API;
+    } else {
+      this.api = `${window.location.protocol}//${window.location.host}`;
+    }
+
+    this.action = this.api + this.url;
+    this.headers[tokenName] = this.$baseAccessToken();
+  },
+  methods: {
+    submitUpload() {
+      this.$refs.upload.submit();
+    },
+    handleProgress(event, file, fileList) {
+      this.loading = true;
+      this.show = true;
+    },
+    handleChange(file, fileList) {
+      if (file.size > 1048576 * this.size) {
+        fileList.map((item, index) => {
+          if (item === file) {
+            fileList.splice(index, 1);
+          }
+        });
+        this.fileList = fileList;
+      } else {
+        this.allImgNum = fileList.length;
+      }
+    },
+    handleSuccess(response, file, fileList) {
+      this.imgNum = this.imgNum + 1;
+      this.imgSuccessNum = this.imgSuccessNum + 1;
+      if (fileList.length === this.imgNum) {
+        setTimeout(() => {
+          this.$emit("fetchDatas");
+          this.$baseMessage(
+            `上传完成! 共上传${fileList.length}张图片`,
+            "success"
+          );
+        }, 1000);
+      }
+
+      setTimeout(() => {
+        this.loading = false;
+        this.show = false;
+      }, 1000);
+    },
+    handleError(err, file, fileList) {
+      this.imgNum = this.imgNum + 1;
+      this.imgErrorNum = this.imgErrorNum + 1;
+      this.$baseMessage(
+        `文件[${file.raw.name}]上传失败,文件大小为${this.$baseLodash.round(
+          file.raw.size / 1024,
+          0
+        )}KB`,
+        "error"
+      );
+      setTimeout(() => {
+        this.loading = false;
+        this.show = false;
+      }, 1000);
+    },
+    handleRemove(file, fileList) {
+      this.imgNum = this.imgNum - 1;
+      this.allNum = this.allNum - 1;
+    },
+    handlePreview(file) {
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    handleExceed(files, fileList) {
+      this.$baseMessage(
+        `当前限制选择 ${this.limit} 个文件,本次选择了
+             ${files.length}
+             个文件`,
+        "error"
+      );
+    },
+    handleShow(data) {
+      this.title = "上传";
+      this.data = data;
+      this.dialogFormVisible = true;
+    },
+    handleClose() {
+      this.fileList = [];
+      this.picture = "picture";
+      this.allImgNum = 0;
+      this.imgNum = 0;
+      this.imgSuccessNum = 0;
+      this.imgErrorNum = 0;
+      if ("development" === process.env.NODE_ENV) {
+        this.api = process.env.VUE_APP_BASE_API;
+      } else {
+        this.api = `${window.location.protocol}//${window.location.host}`;
+      }
+
+      this.action = this.api + this.url;
+      this.headers[tokenName] = this.$baseAccessToken();
+      this.dialogFormVisible = false;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.upload {
+  height: 600px;
+
+  .upload-content {
+    .el-upload__tip {
+      display: block;
+      height: 30px;
+      line-height: 30px;
+    }
+
+    ::v-deep {
+      .el-upload--picture-card {
+        width: 128px;
+        height: 128px;
+        margin: 3px 8px 8px 8px;
+        border: 2px dashed #c0ccda;
+      }
+
+      .el-upload-list--picture {
+        margin-bottom: 20px;
+      }
+
+      .el-upload-list--picture-card {
+        .el-upload-list__item {
+          width: 128px;
+          height: 128px;
+          margin: 3px 8px 8px 8px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 85 - 0
src/config/permission.js

@@ -0,0 +1,85 @@
+/**
+ * @copyright chuzhixin 1204505056@qq.com
+ * @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,
+  routesWhiteList,
+  progressBar,
+  recordRoute,
+} from "./settings";
+
+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时,创建虚拟权限
+            store.dispatch("user/setPermissions", ["admin"]);
+            permissions = ["admin"];
+          } else {
+            permissions = await store.dispatch("user/getInfo");
+          }
+
+          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();
+});

+ 99 - 0
src/config/settings.js

@@ -0,0 +1,99 @@
+/**
+ * @copyright chuzhixin 1204505056@qq.com
+ * @description 全局变量配置
+ */
+module.exports = {
+  // 开发以及部署时的URL
+  publicPath: "",
+  // 生产环境构建文件的目录名
+  outputDir: "dist",
+  // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。
+  assetsDir: "static",
+  // 开发环境每次保存时是否输出为eslint编译警告
+  lintOnSave: true,
+  // 进行编译的依赖
+  transpileDependencies: ["vue-echarts", "resize-detector", "zx-layouts"],
+  // 默认的接口地址 如果是开发环境和生产环境走vab-mock-server,当然你也可以选择自己配置成需要的接口地址
+  baseURL:
+    process.env.NODE_ENV === "development" || process.env.NODE_ENV === "preview"
+      ? "vab-mock-server"
+      : "http://your.website.com",
+  //标题 (包括初次加载雪花屏的标题 页面的标题 浏览器的标题)
+  title: "vue-admin-beautiful",
+  //简写
+  abbreviation: "vab",
+  //开发环境端口号
+  devPort: "80",
+  //版本号
+  version: process.env.VUE_APP_VERSION,
+  //烦请保留package.json作者信息 保留版权可免费商用 如需去除并自定义为自己企业的版权请联系群主QQ 1204505056 需支付299元 恶意修改发生纠纷及出现任何问题 由修改人自行承担
+  copyright: process.env.VUE_APP_AUTHOR,
+  //是否显示页面底部版权信息,建议您显示,当然您也可以选择不显示,不管您是付费用户还是未付费用户您都有选择显示或者不显示的权利
+  footerCopyright: process.env.NODE_ENV !== "development" ? true : false,
+  //是否显示右上角github图标
+  githubCorner: process.env.NODE_ENV !== "development" ? true : false,
+  //是否显示顶部进度条
+  progressBar: true,
+  //缓存路由的最大数量
+  keepAliveMaxNum: 99,
+  // 路由模式,可选值为 history 或 hash
+  routerMode: "hash",
+  //不经过token校验的路由
+  routesWhiteList: ["/login", "/register", "/404", "/401"],
+  //加载时显示文字
+  loadingText: "正在加载中...",
+  //token名称
+  tokenName: "accessToken",
+  //token在localStorage、sessionStorage、cookie存储的key的名称
+  tokenTableName: "vue-admin-beautiful",
+  //token存储位置localStorage sessionStorage cookie
+  storage: "localStorage",
+  //token失效回退到登录页时是否记录本次的路由
+  recordRoute: true,
+  //是否显示logo,不显示时设置false,显示时请填写remixIcon图标名称,暂时只支持设置remixIcon
+  logo: "vuejs-fill",
+  //是否国定头部 固定fixed 不固定noFixed
+  header: "fixed",
+  //横纵布局 horizontal vertical
+  layout: "vertical",
+  //是否开启主题配置按钮
+  themeBar: true,
+  //是否显示多标签页
+  tagsBar: true,
+  //是否显示骨架屏
+  skeleton: false,
+  //配后端数据的接收方式application/json;charset=UTF-8或者application/x-www-form-urlencoded;charset=UTF-8
+  contentType: "application/json;charset=UTF-8",
+  //消息框消失时间
+  messageDuration: 3000,
+  //最长请求时间
+  requestTimeout: 5000,
+  //操作正常code
+  successCode: 200,
+  //登录失效code
+  invalidCode: 402,
+  //无权限code
+  noPermissionCode: 401,
+  //是否显示在页面高亮错误
+  errorLog: ["development", "test", "production"],
+  //是否开启登录拦截
+  loginInterception: true,
+  //是否开启登录RSA加密
+  loginRSA: false,
+  //是否依据mock数据生成webstorm HTTP Request请求文件
+  httpRequestFile: 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",
+};

+ 0 - 0
src/directive/drag/drag.js


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor