wangwei 4 years ago
commit
16048479c5
100 changed files with 3519 additions and 0 deletions
  1. 18 0
      .editorconfig
  2. 8 0
      .env
  3. 25 0
      .env.development
  4. 32 0
      .env.production
  5. 15 0
      .eslintignore
  6. 69 0
      .eslintrc.js
  7. 29 0
      .gitignore
  8. 1 0
      .husky/.gitignore
  9. 6 0
      .husky/commit-msg
  10. 9 0
      .husky/common.sh
  11. 8 0
      .husky/lintstagedrc.js
  12. 14 0
      .husky/pre-commit
  13. 23 0
      .ls-lint.yml
  14. 9 0
      .prettierignore
  15. 2 0
      .stylelintignore
  16. 14 0
      .vscode/extensions.json
  17. 5 0
      .vscode/i18n-ally-reviews.yml
  18. 13 0
      .vscode/launch.json
  19. 199 0
      .vscode/settings.json
  20. 48 0
      .yarnclean
  21. 21 0
      LICENSE
  22. 34 0
      README.md
  23. 104 0
      build/config/themeConfig.ts
  24. 6 0
      build/constant.ts
  25. 9 0
      build/getShortName.ts
  26. 44 0
      build/script/buildConf.ts
  27. 23 0
      build/script/postBuild.ts
  28. 18 0
      build/tsconfig.json
  29. 4 0
      build/typeing.d.ts
  30. 92 0
      build/utils.ts
  31. 22 0
      build/vite/optimizer.ts
  32. 30 0
      build/vite/plugin/compress.ts
  33. 43 0
      build/vite/plugin/html.ts
  34. 37 0
      build/vite/plugin/imagemin.ts
  35. 67 0
      build/vite/plugin/index.ts
  36. 20 0
      build/vite/plugin/mock.ts
  37. 36 0
      build/vite/plugin/pwa.ts
  38. 21 0
      build/vite/plugin/styleImport.ts
  39. 19 0
      build/vite/plugin/theme.ts
  40. 15 0
      build/vite/plugin/visualizer.ts
  41. 12 0
      build/vite/plugin/windicss.ts
  42. 34 0
      build/vite/proxy.ts
  43. 56 0
      commitlint.config.js
  44. 0 0
      favicon.ico
  45. 148 0
      index.html
  46. 18 0
      mock/_createProductionServer.ts
  47. 47 0
      mock/_util.ts
  48. 25 0
      mock/demo/select-demo.ts
  49. 151 0
      mock/demo/system.ts
  50. 38 0
      mock/demo/table-demo.ts
  51. 280 0
      mock/sys/menu.ts
  52. 107 0
      mock/sys/user.ts
  53. 130 0
      package.json
  54. 5 0
      postcss.config.js
  55. 20 0
      prettier.config.js
  56. BIN
      public/favicon.ico
  57. BIN
      public/resource/img/logo.png
  58. BIN
      public/resource/img/pwa-192x192.png
  59. BIN
      public/resource/img/pwa-512x512.png
  60. 389 0
      public/resource/tinymce/langs/zh_CN.js
  61. 38 0
      src/App.vue
  62. 11 0
      src/api/demo/model/optionsModel.ts
  63. 74 0
      src/api/demo/model/systemModel.ts
  64. 20 0
      src/api/demo/model/tableModel.ts
  65. 16 0
      src/api/demo/select.ts
  66. 36 0
      src/api/demo/system.ts
  67. 20 0
      src/api/demo/table.ts
  68. 9 0
      src/api/model/baseModel.ts
  69. 68 0
      src/api/sys/menu.ts
  70. 72 0
      src/api/sys/model/menuModel.ts
  71. 5 0
      src/api/sys/model/uploadModel.ts
  72. 149 0
      src/api/sys/model/userModel.ts
  73. 27 0
      src/api/sys/upload.ts
  74. 161 0
      src/api/sys/user.ts
  75. BIN
      src/assets/images/dashboard/wokb/approve.png
  76. BIN
      src/assets/images/dashboard/wokb/attendance.png
  77. BIN
      src/assets/images/dashboard/wokb/datashow1.png
  78. BIN
      src/assets/images/dashboard/wokb/datashow2.png
  79. BIN
      src/assets/images/dashboard/wokb/datashow3.png
  80. BIN
      src/assets/images/dashboard/wokb/datashow4.png
  81. BIN
      src/assets/images/dashboard/wokb/leave.png
  82. BIN
      src/assets/images/dashboard/wokb/meal.png
  83. BIN
      src/assets/images/dashboard/wokb/overtime.png
  84. BIN
      src/assets/images/dashboard/wokb/performance.png
  85. BIN
      src/assets/images/dashboard/wokb/stamp.png
  86. BIN
      src/assets/images/dashboard/wokb/travel.png
  87. BIN
      src/assets/images/dashboard/wokb/wokb.png
  88. BIN
      src/assets/images/demo.png
  89. BIN
      src/assets/images/header.jpg
  90. BIN
      src/assets/images/logo.png
  91. 20 0
      src/assets/svg/dashboard/analysis-down.svg
  92. 21 0
      src/assets/svg/dashboard/analysis-icon1.svg
  93. 21 0
      src/assets/svg/dashboard/analysis-icon2.svg
  94. 21 0
      src/assets/svg/dashboard/analysis-icon3.svg
  95. 21 0
      src/assets/svg/dashboard/analysis-icon4.svg
  96. 20 0
      src/assets/svg/dashboard/analysis-rise.svg
  97. 17 0
      src/assets/svg/login-bg.svg
  98. 0 0
      src/assets/svg/login-box-bg.svg
  99. 0 0
      src/assets/svg/net-error.svg
  100. 0 0
      src/assets/svg/no-data.svg

+ 18 - 0
.editorconfig

@@ -0,0 +1,18 @@
+root = true
+
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=true
+indent_style=space
+indent_size=2
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab

+ 8 - 0
.env

@@ -0,0 +1,8 @@
+# port
+VITE_PORT = 3200
+
+# spa-title
+VITE_GLOB_APP_TITLE = admin
+
+# spa shortname
+VITE_GLOB_APP_SHORT_NAME = vue_vben_admin_2x

+ 25 - 0
.env.development

@@ -0,0 +1,25 @@
+VITE_PORT = 3100
+
+# Whether to open mock
+VITE_USE_MOCK = true
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Cross-domain proxy, you can configure multiple
+VITE_PROXY=[["/api","http://localhost:3000"],["/upload","http://localhost:3001/upload"]]
+# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
+
+# Delete console
+VITE_DROP_CONSOLE = false
+
+# Basic interface address SPA
+# VITE_GLOB_API_URL= http://localhost:8000/api
+VITE_GLOB_API_URL= http://localhost:8888/admin
+# VITE_GLOB_API_URL=/api
+
+# File upload address, optional
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=

+ 32 - 0
.env.production

@@ -0,0 +1,32 @@
+# Whether to open mock
+VITE_USE_MOCK = true
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Delete console
+VITE_DROP_CONSOLE = true
+
+# Whether to enable gizp or brotli compression
+# Optional: gzip | brotli | none
+# If you need multiple forms, you can use `,` to separate
+VITE_BUILD_COMPRESS = 'none'
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/api
+
+# File upload address, optional
+# It can be forwarded by nginx or write the actual address directly
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=
+
+# Whether to enable image compression
+VITE_USE_IMAGEMIN= true
+
+# use pwa
+VITE_USE_PWA = false
+
+# Is it compatible with older browsers
+VITE_LEGACY = false

+ 15 - 0
.eslintignore

@@ -0,0 +1,15 @@
+
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+/public
+/docs
+.husky
+.local
+/bin
+Dockerfile

+ 69 - 0
.eslintrc.js

@@ -0,0 +1,69 @@
+module.exports = {
+  parser: 'vue-eslint-parser',
+  parserOptions: {
+    parser: '@typescript-eslint/parser',
+    ecmaVersion: 2020,
+    sourceType: 'module',
+    ecmaFeatures: {
+      jsx: true,
+    },
+  },
+
+  extends: [
+    'plugin:vue/vue3-recommended',
+    'plugin:@typescript-eslint/recommended',
+    'prettier/@typescript-eslint',
+    'plugin:prettier/recommended',
+  ],
+  rules: {
+    '@typescript-eslint/ban-ts-ignore': 'off',
+    '@typescript-eslint/explicit-function-return-type': 'off',
+    '@typescript-eslint/no-explicit-any': 'off',
+    '@typescript-eslint/no-var-requires': 'off',
+    '@typescript-eslint/no-empty-function': 'off',
+    'vue/custom-event-name-casing': 'off',
+    'no-use-before-define': 'off',
+    '@typescript-eslint/no-use-before-define': 'off',
+    '@typescript-eslint/ban-ts-comment': 'off',
+    '@typescript-eslint/ban-types': 'off',
+    '@typescript-eslint/no-non-null-assertion': 'off',
+    '@typescript-eslint/explicit-module-boundary-types': 'off',
+    '@typescript-eslint/no-unused-vars': [
+      'error',
+      {
+        argsIgnorePattern: '^h$',
+        varsIgnorePattern: '^h$',
+      },
+    ],
+    'no-unused-vars': [
+      'error',
+      {
+        argsIgnorePattern: '^h$',
+        varsIgnorePattern: '^h$',
+      },
+    ],
+    'space-before-function-paren': 'off',
+
+    'vue/attributes-order': 'off',
+    'vue/one-component-per-file': 'off',
+    'vue/html-closing-bracket-newline': 'off',
+    'vue/max-attributes-per-line': 'off',
+    'vue/multiline-html-element-content-newline': 'off',
+    'vue/singleline-html-element-content-newline': 'off',
+    'vue/attribute-hyphenation': 'off',
+    // 'vue/html-self-closing': 'off',
+    'vue/require-default-prop': 'off',
+    'vue/html-self-closing': [
+      'error',
+      {
+        html: {
+          void: 'always',
+          normal: 'never',
+          component: 'always',
+        },
+        svg: 'always',
+        math: 'always',
+      },
+    ],
+  },
+};

+ 29 - 0
.gitignore

@@ -0,0 +1,29 @@
+node_modules
+.DS_Store
+dist
+.npmrc
+.cache
+
+test/upload-server/static
+
+.local
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+# .vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.history/src/api/sys/user_20210610150327.ts
+.history/

+ 1 - 0
.husky/.gitignore

@@ -0,0 +1 @@
+_

+ 6 - 0
.husky/commit-msg

@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# shellcheck source=./_/husky.sh
+. "$(dirname "$0")/_/husky.sh"
+
+npx --no-install commitlint --edit "$1"

+ 9 - 0
.husky/common.sh

@@ -0,0 +1,9 @@
+#!/bin/sh
+command_exists () {
+  command -v "$1" >/dev/null 2>&1
+}
+
+# Workaround for Windows 10, Git Bash and Yarn
+if command_exists winpty && test -t 1; then
+  exec < /dev/tty
+fi

+ 8 - 0
.husky/lintstagedrc.js

@@ -0,0 +1,8 @@
+module.exports = {
+  '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
+  '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
+  'package.json': ['prettier --write'],
+  '*.vue': ['prettier --write', 'stylelint --fix'],
+  '*.{scss,less,styl,css,html}': ['stylelint --fix', 'prettier --write'],
+  '*.md': ['prettier --write'],
+};

+ 14 - 0
.husky/pre-commit

@@ -0,0 +1,14 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+. "$(dirname "$0")/common.sh"
+
+[ -n "$CI" ] && exit 0
+
+# Check the file name
+# ! ls-lint cannot be used normally in mac pro of M1 system.
+npm run lint:ls-lint
+
+# Format and submit code according to lintstagedrc.js configuration
+npm run lint:lint-staged
+
+npm run lint:pretty

+ 23 - 0
.ls-lint.yml

@@ -0,0 +1,23 @@
+ls:
+  src/*:
+    .js: kebab-case | PascalCase
+    .vue: PascalCase | regex:^index
+    .ts: camelCase | PascalCase
+    .tsx: camelCase | PascalCase
+    .d.ts: kebab-case
+    .mock.ts: kebab-case
+    .data.ts: camelCase | kebab-case
+    .test-d.ts: kebab-case
+    .less: kebab-case | PascalCase
+    .spec.ts: camelCase | PascalCase
+
+ignore:
+  - node_modules
+  - .git
+  - .circleci
+  - .github
+  - .vscode
+  - .idea
+  - dist
+  - .local
+  - .husky

+ 9 - 0
.prettierignore

@@ -0,0 +1,9 @@
+/dist/*
+.local
+.output.js
+/node_modules/**
+
+**/*.svg
+**/*.sh
+
+/public/*

+ 2 - 0
.stylelintignore

@@ -0,0 +1,2 @@
+/dist/*
+/public/*

+ 14 - 0
.vscode/extensions.json

@@ -0,0 +1,14 @@
+{
+  "recommendations": [
+    "octref.vetur",
+    "dbaeumer.vscode-eslint",
+    "stylelint.vscode-stylelint",
+    "esbenp.prettier-vscode",
+    "mrmlnc.vscode-less",
+    "antfu.i18n-ally",
+    "antfu.iconify",
+    "mikestead.dotenv",
+    "bradlc.vscode-tailwindcss",
+    "heybourn.headwind"
+  ]
+}

+ 5 - 0
.vscode/i18n-ally-reviews.yml

@@ -0,0 +1,5 @@
+# Review comments generated by i18n-ally. Please commit this file.
+
+reviews:
+  sys.login.autoLogin:
+    description: '1'

+ 13 - 0
.vscode/launch.json

@@ -0,0 +1,13 @@
+{
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "type": "chrome",
+      "request": "launch",
+      "name": "Launch Chrome",
+      "url": "http://localhost:3100",
+      "webRoot": "${workspaceFolder}/src",
+      "sourceMaps": true
+    },
+  ]
+}

+ 199 - 0
.vscode/settings.json

@@ -0,0 +1,199 @@
+{
+  "typescript.tsdk": "./node_modules/typescript/lib",
+  "volar.tsPlugin": true,
+  "volar.tsPluginStatus": false,
+  //===========================================
+  //============= Editor ======================
+  //===========================================
+  "explorer.openEditors.visible": 0,
+  "editor.minimap.renderCharacters": false,
+  "editor.minimap.maxColumn": 300,
+  "editor.minimap.showSlider": "always",
+  "editor.smoothScrolling": true,
+  "editor.cursorBlinking": "phase",
+  "editor.cursorSmoothCaretAnimation": true,
+  "editor.detectIndentation": false,
+  "diffEditor.ignoreTrimWhitespace": false,
+  "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
+  "editor.formatOnPaste": true,
+  "editor.formatOnSave": true,
+  "editor.suggestSelection": "first",
+  "editor.trimAutoWhitespace": true,
+  "editor.quickSuggestions": {
+    "other": true,
+    "comments": true,
+    "strings": true
+  },
+  //===========================================
+  //============= Other =======================
+  //===========================================
+  "breadcrumbs.enabled": true,
+  "open-in-browser.default": "chrome",
+  //===========================================
+  //============= emmet =======================
+  //===========================================
+  "emmet.triggerExpansionOnTab": true,
+  "emmet.showAbbreviationSuggestions": true,
+  "emmet.showExpandedAbbreviation": "always",
+  "emmet.syntaxProfiles": {
+    "vue-html": "html",
+    "vue": "html",
+    "javascript": "javascriptreact",
+    "xml": {
+      "attr_quotes": "single"
+    }
+  },
+  "emmet.includeLanguages": {
+    "jsx-sublime-babel-tags": "javascriptreact"
+  },
+  //===========================================
+  //============= files =======================
+  //===========================================
+  "files.trimTrailingWhitespace": true,
+  "files.insertFinalNewline": true,
+  "files.trimFinalNewlines": true,
+  "files.eol": "\n",
+  "search.exclude": {
+    "**/node_modules": true,
+    "**/*.log": true,
+    "**/*.log*": true,
+    "**/bower_components": true,
+    "**/dist": true,
+    "**/elehukouben": true,
+    "**/.git": true,
+    "**/.gitignore": true,
+    "**/.svn": true,
+    "**/.DS_Store": true,
+    "**/.idea": true,
+    "**/.vscode": false,
+    "**/yarn.lock": true,
+    "**/tmp": true,
+    "out": true,
+    "dist": true,
+    "node_modules": true,
+    "CHANGELOG.md": true,
+    "examples": true,
+    "res": true,
+    "screenshots": true
+  },
+  "files.exclude": {
+    "**/bower_components": true,
+    "**/.idea": true,
+    "**/yarn.lock": true,
+    "**/tmp": true,
+    "**/.git": true,
+    "**/.svn": true,
+    "**/.hg": true,
+    "**/CVS": true,
+    "**/.DS_Store": true
+  },
+  "files.watcherExclude": {
+    // 文件监视排除
+    "**/.git/objects/**": true,
+    "**/.git/subtree-cache/**": true,
+    "**/.vscode/**": true,
+    "**/node_modules/**": true,
+    "**/tmp/**": true,
+    "**/bower_components/**": true,
+    "**/dist/**": true,
+    "**/yarn.lock": true
+  },
+  "files.associations": {
+    "*.vue": "vue",
+    "*.wxss": "css"
+  },
+  "stylelint.enable": true,
+  "stylelint.packageManager": "yarn",
+  "css.validate": true,
+  "less.validate": true,
+  "scss.validate": true,
+  // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
+  // ===========================================
+  // ================ Eslint ===================
+  // ===========================================
+  // "eslint.enable": true,
+  "eslint.alwaysShowStatus": true,
+  "eslint.options": {
+    // 配置
+    "plugins": ["html", "vue", "javascript", "jsx", "typescript"],
+    "extensions": [".js", ".jsx", ".ts", ".tsx", ".vue"]
+  },
+  "eslint.validate": [
+    "javascript",
+    "typescript",
+    "reacttypescript",
+    "reactjavascript",
+    "html",
+    "vue"
+  ],
+  // "eslint.autoFixOnSave": true,
+  // ===========================================
+  // ================ Vetur ====================
+  // ===========================================
+  "vetur.experimental.templateInterpolationService": true,
+  "vetur.format.options.tabSize": 2,
+  "vetur.format.defaultFormatter.html": "js-beautify-html",
+  "vetur.format.defaultFormatter.scss": "prettier",
+  "vetur.format.defaultFormatter.css": "prettier",
+  "vetur.format.defaultFormatter.ts": "prettier-tslint",
+  "vetur.format.defaultFormatter.js": "prettier",
+  "vetur.languageFeatures.codeActions": false,
+  "vetur.format.defaultFormatterOptions": {
+    "js-beautify-html": {
+      "wrap_attributes": "force-expand-multiline"
+    },
+    "prettier": {
+      "eslintIntegration": true,
+      "arrowParens": "always",
+      "semi": false,
+      "singleQuote": true
+    }
+  },
+  "javascript.updateImportsOnFileMove.enabled": "never",
+  "liveServer.settings.donotShowInfoMsg": true,
+  "terminal.integrated.rendererType": "dom",
+  "telemetry.enableCrashReporter": false,
+  "telemetry.enableTelemetry": false,
+  "workbench.settings.enableNaturalLanguageSearch": false,
+  "path-intellisense.mappings": {
+    "/@/": "${workspaceRoot}/src"
+  },
+  "prettier.requireConfig": true,
+  "typescript.updateImportsOnFileMove.enabled": "always",
+  "workbench.sideBar.location": "left",
+  "[javascriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescriptreact]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[html]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[css]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[less]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[scss]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[markdown]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "editor.codeActionsOnSave": {
+    "source.fixAll.eslint": true
+  },
+  "i18n-ally.localesPaths": ["src/locales/lang"],
+  "i18n-ally.keystyle": "nested",
+  "i18n-ally.sortKeys": true,
+  "i18n-ally.namespace": true,
+  "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
+  "i18n-ally.enabledParsers": ["ts"],
+  "i18n-ally.sourceLanguage": "zh",
+  "i18n-ally.enabledFrameworks": ["vue", "react"]
+}

+ 48 - 0
.yarnclean

@@ -0,0 +1,48 @@
+# test directories
+__tests__
+test
+tests
+powered-test
+
+# asset directories
+docs
+doc
+website
+images
+assets
+
+# examples
+example
+examples
+
+# code coverage directories
+coverage
+.nyc_output
+
+# build scripts
+Makefile
+Gulpfile.js
+Gruntfile.js
+
+# configs
+appveyor.yml
+circle.yml
+codeship-services.yml
+codeship-steps.yml
+wercker.yml
+.tern-project
+.gitattributes
+.editorconfig
+.*ignore
+.eslintrc
+.jshintrc
+.flowconfig
+.documentup.json
+.yarn-metadata.json
+.travis.yml
+
+# misc
+*.md
+
+!istanbul-reports/lib/html/assets
+!istanbul-api/node_modules/istanbul-reports/lib/html/assets

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020-present, Vben
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 34 - 0
README.md

@@ -0,0 +1,34 @@
+## Preinstallation
+
+### Environmental requirements
+
+- `Node.js`: - Version > `12.0.0` .
+- `yarn` : - Package management tool.
+
+当你开始使用时,请按下面列表先行修改项目
+
+- [ ] 重命名 `package.json` 中的 `name` 字段
+- [ ] 在`LICENSE`中更改作者姓名
+- [ ] 在`public`中修改 `favicon.ico`
+- [ ] 在`public/resource/`和`/src/assets/images/logo.png`中修改 `logo.png`
+- [ ] 在`.env[xxx]`文件中修改相关项目配置
+- [ ] 在`src/settings/projectSetting.ts`内调整适合自己的项目风格
+
+## server django 后台启动
+
+- cd server
+- py manage.py runserver `默认端口8000`
+
+## 注意
+
+依赖删除了`echarts`,`apexcharts`,`xlsx`,`vditor`。但是组件及代码未删除。在你未引用到相关组件的时候,不会发出错误。当你需要使用的时候,只需要执行相应的命令安装对应模块即可
+
+需要用到哪个则执行对应命令
+
+```js
+
+yarn add echarts
+
+yarn add apexcharts
+
+```

+ 104 - 0
build/config/themeConfig.ts

@@ -0,0 +1,104 @@
+import { generate } from '@ant-design/colors';
+
+export const primaryColor = '#0960bd';
+
+export const themeMode = 'light';
+
+export type ThemeMode = 'dark' | 'light';
+
+type Fn = (...arg: any) => any;
+
+export interface GenerateColorsParams {
+  mixLighten: Fn;
+  mixDarken: Fn;
+  tinycolor: any;
+  color?: string;
+}
+
+export function generateAntColors(color: string, mode: ThemeMode) {
+  return generate(color, {
+    theme: mode == 'dark' ? 'dark' : 'default',
+  });
+}
+
+export function getThemeColors(color?: string, theme?: ThemeMode) {
+  const tc = color || primaryColor;
+  const tm = theme || themeMode;
+  const colors = generateAntColors(tc, tm);
+  const primary = colors[5];
+  const modeColors = generateAntColors(primary, tm === 'dark' ? 'light' : 'dark');
+
+  return [...colors, ...modeColors];
+}
+
+export function generateColors({
+  color = primaryColor,
+  mixLighten,
+  mixDarken,
+  tinycolor,
+}: GenerateColorsParams) {
+  const arr = new Array(19).fill(0);
+  const lightens = arr.map((t, i) => {
+    return mixLighten(color, i / 5);
+  });
+
+  const darkens = arr.map((t, i) => {
+    return mixDarken(color, i / 5);
+  });
+
+  const alphaColors = arr.map((t, i) => {
+    return tinycolor(color)
+      .setAlpha(i / 20)
+      .toRgbString();
+  });
+
+  const tinycolorLightens = arr
+    .map((t, i) => {
+      return tinycolor(color)
+        .lighten(i * 5)
+        .toHexString();
+    })
+    .filter((item) => item !== '#ffffff');
+
+  const tinycolorDarkens = arr
+    .map((t, i) => {
+      return tinycolor(color)
+        .darken(i * 5)
+        .toHexString();
+    })
+    .filter((item) => item !== '#000000');
+  return [...lightens, ...darkens, ...alphaColors, ...tinycolorDarkens, ...tinycolorLightens];
+}
+
+/**
+ * less global variable
+ */
+export function generateModifyVars() {
+  const palettes = generateAntColors(primaryColor, themeMode);
+  const primary = palettes[5];
+
+  const primaryColorObj: Record<string, string> = {};
+
+  for (let index = 0; index < 10; index++) {
+    primaryColorObj[`primary-${index + 1}`] = palettes[index];
+  }
+
+  return {
+    'primary-color': primary,
+    ...primaryColorObj,
+    'info-color': primary,
+    'processing-color': primary,
+    'success-color': '#55D187', //  Success color
+    'error-color': '#ED6F6F', //  False color
+    'warning-color': '#EFBD47', //   Warning color
+    'disabled-color': 'rgba(0, 0, 0, 0.25)', //  Failure color
+    'heading-color': 'rgba(0, 0, 0, 0.85)', //  Title color
+    'text-color': 'rgba(0, 0, 0, 0.85)', //  Main text color
+    'text-color-secondary': 'rgba(0, 0, 0, 0.45)', // Subtext color
+    'font-size-base': '14px', //  Main font size
+    'box-shadow-base': '0 2px 8px rgba(0, 0, 0, 0.15)', //  Floating shadow
+    'border-color-base': '#d9d9d9', //  Border color,
+    'border-radius-base': '2px', //  Component/float fillet
+    'link-color': primary, //   Link color
+  };
+}

+ 6 - 0
build/constant.ts

@@ -0,0 +1,6 @@
+/**
+ * The name of the configuration file entered in the production environment
+ */
+export const GLOB_CONFIG_FILE_NAME = '_app.config.js';
+
+export const OUTPUT_DIR = 'dist';

+ 9 - 0
build/getShortName.ts

@@ -0,0 +1,9 @@
+/**
+ * Get the configuration file variable name
+ * @param env
+ */
+export const getShortName = (env: any) => {
+  return `__PRODUCTION__${env.VITE_GLOB_APP_SHORT_NAME || '__APP'}__CONF__`
+    .toUpperCase()
+    .replace(/\s/g, '');
+};

+ 44 - 0
build/script/buildConf.ts

@@ -0,0 +1,44 @@
+/**
+ * Generate additional configuration files when used for packaging. The file can be configured with some global variables, so that it can be changed directly externally without repackaging
+ */
+import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
+import fs, { writeFileSync } from 'fs-extra';
+import chalk from 'chalk';
+
+import { getCwdPath, getEnvConfig } from '../utils';
+import { getShortName } from '../getShortName';
+
+import pkg from '../../package.json';
+
+function createConfig(
+  {
+    configName,
+    config,
+    configFileName = GLOB_CONFIG_FILE_NAME,
+  }: { configName: string; config: any; configFileName?: string } = { configName: '', config: {} }
+) {
+  try {
+    const windowConf = `window.${configName}`;
+    // Ensure that the variable will not be modified
+    const configStr = `${windowConf}=${JSON.stringify(config)};
+      Object.freeze(${windowConf});
+      Object.defineProperty(window, "${configName}", {
+        configurable: false,
+        writable: false,
+      });
+    `.replace(/\s/g, '');
+    fs.mkdirp(getCwdPath(OUTPUT_DIR));
+    writeFileSync(getCwdPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
+
+    console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
+    console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n');
+  } catch (error) {
+    console.log(chalk.red('configuration file configuration file failed to package:\n' + error));
+  }
+}
+
+export function runBuildConfig() {
+  const config = getEnvConfig();
+  const configFileName = getShortName(config);
+  createConfig({ config, configName: configFileName });
+}

+ 23 - 0
build/script/postBuild.ts

@@ -0,0 +1,23 @@
+// #!/usr/bin/env node
+
+import { argv } from 'yargs';
+import { runBuildConfig } from './buildConf';
+import chalk from 'chalk';
+
+import pkg from '../../package.json';
+
+export const runBuild = async () => {
+  try {
+    const argvList = argv._;
+
+    // Generate configuration file
+    if (!argvList.includes('no-conf')) {
+      await runBuildConfig();
+    }
+    console.log(`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
+  } catch (error) {
+    console.log(chalk.red('vite build error:\n' + error));
+    process.exit(1);
+  }
+};
+runBuild();

+ 18 - 0
build/tsconfig.json

@@ -0,0 +1,18 @@
+{
+  "compilerOptions": {
+    "target": "esnext",
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "jsx": "react",
+    "baseUrl": ".",
+    "esModuleInterop": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "experimentalDecorators": true,
+    "lib": ["dom", "esnext"],
+    "incremental": true,
+    "skipLibCheck": true
+  }
+}

+ 4 - 0
build/typeing.d.ts

@@ -0,0 +1,4 @@
+declare module '*.json' {
+  const src: any;
+  export default src;
+}

+ 92 - 0
build/utils.ts

@@ -0,0 +1,92 @@
+import fs from 'fs';
+import path from 'path';
+import dotenv from 'dotenv';
+
+export const isFunction = (arg: unknown): arg is (...args: any[]) => any =>
+  typeof arg === 'function';
+
+export const isRegExp = (arg: unknown): arg is RegExp =>
+  Object.prototype.toString.call(arg) === '[object RegExp]';
+
+export function isDevFn(mode: string): boolean {
+  return mode === 'development';
+}
+
+export function isProdFn(mode: string): boolean {
+  return mode === 'production';
+}
+
+/**
+ * Whether to generate package preview
+ */
+export function isReportMode(): boolean {
+  return process.env.REPORT === 'true';
+}
+
+export interface ViteEnv {
+  VITE_PORT: number;
+  VITE_USE_MOCK: boolean;
+  VITE_USE_PWA: boolean;
+  VITE_PUBLIC_PATH: string;
+  VITE_PROXY: [string, string][];
+  VITE_GLOB_APP_TITLE: string;
+  VITE_GLOB_APP_SHORT_NAME: string;
+  VITE_USE_CDN: boolean;
+  VITE_DROP_CONSOLE: boolean;
+  VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
+  VITE_DYNAMIC_IMPORT: boolean;
+  VITE_LEGACY: boolean;
+  VITE_USE_IMAGEMIN: boolean;
+}
+
+// Read all environment variable configuration files to process.env
+export function wrapperEnv(envConf: any): ViteEnv {
+  const ret: any = {};
+
+  for (const envName of Object.keys(envConf)) {
+    let realName = envConf[envName].replace(/\\n/g, '\n');
+    realName = realName === 'true' ? true : realName === 'false' ? false : realName;
+    if (envName === 'VITE_PORT') {
+      realName = Number(realName);
+    }
+    if (envName === 'VITE_PROXY') {
+      try {
+        realName = JSON.parse(realName);
+      } catch (error) {}
+    }
+    ret[envName] = realName;
+    process.env[envName] = realName;
+  }
+  return ret;
+}
+
+/**
+ * Get the environment variables starting with the specified prefix
+ * @param match prefix
+ * @param confFiles ext
+ */
+export function getEnvConfig(match = 'VITE_GLOB_', confFiles = ['.env', '.env.production']) {
+  let envConfig = {};
+  confFiles.forEach((item) => {
+    try {
+      const env = dotenv.parse(fs.readFileSync(path.resolve(process.cwd(), item)));
+
+      envConfig = { ...envConfig, ...env };
+    } catch (error) {}
+  });
+  Object.keys(envConfig).forEach((key) => {
+    const reg = new RegExp(`^(${match})`);
+    if (!reg.test(key)) {
+      Reflect.deleteProperty(envConfig, key);
+    }
+  });
+  return envConfig;
+}
+
+/**
+ * Get user root directory
+ * @param dir file path
+ */
+export function getCwdPath(...dir: string[]) {
+  return path.resolve(process.cwd(), ...dir);
+}

+ 22 - 0
build/vite/optimizer.ts

@@ -0,0 +1,22 @@
+// TODO
+import type { GetManualChunk, GetManualChunkApi } from 'rollup';
+
+//
+const vendorLibs: { match: string[]; output: string }[] = [
+  // {
+  //   match: ['xlsx'],
+  //   output: 'xlsx',
+  // },
+];
+
+// @ts-ignore
+export const configManualChunk: GetManualChunk = (id: string, api: GetManualChunkApi) => {
+  console.log(api);
+  if (/[\\/]node_modules[\\/]/.test(id)) {
+    const matchItem = vendorLibs.find((item) => {
+      const reg = new RegExp(`[\\/]node_modules[\\/]_?(${item.match.join('|')})(.*)`, 'ig');
+      return reg.test(id);
+    });
+    return matchItem ? matchItem.output : null;
+  }
+};

+ 30 - 0
build/vite/plugin/compress.ts

@@ -0,0 +1,30 @@
+/**
+ * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
+ * https://github.com/anncwb/vite-plugin-compression
+ */
+import type { Plugin } from 'vite';
+
+import compressPlugin from 'vite-plugin-compression';
+
+export function configCompressPlugin(compress: 'gzip' | 'brotli' | 'none'): Plugin | Plugin[] {
+  const compressList = compress.split(',');
+
+  const plugins: Plugin[] = [];
+
+  if (compressList.includes('gzip')) {
+    plugins.push(
+      compressPlugin({
+        ext: '.gz',
+      })
+    );
+  }
+  if (compressList.includes('brotli')) {
+    plugins.push(
+      compressPlugin({
+        ext: '.br',
+        algorithm: 'brotliCompress',
+      })
+    );
+  }
+  return plugins;
+}

+ 43 - 0
build/vite/plugin/html.ts

@@ -0,0 +1,43 @@
+/**
+ * Plugin to minimize and use ejs template syntax in index.html.
+ * https://github.com/anncwb/vite-plugin-html
+ */
+import type { Plugin } from 'vite';
+import type { ViteEnv } from '../../utils';
+
+import html from 'vite-plugin-html';
+
+import pkg from '../../../package.json';
+import { GLOB_CONFIG_FILE_NAME } from '../../constant';
+
+export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
+  const { VITE_GLOB_APP_TITLE, VITE_PUBLIC_PATH } = env;
+
+  const path = VITE_PUBLIC_PATH.endsWith('/') ? VITE_PUBLIC_PATH : `${VITE_PUBLIC_PATH}/`;
+
+  const getAppConfigSrc = () => {
+    return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
+  };
+
+  const htmlPlugin: Plugin[] = html({
+    minify: isBuild,
+    inject: {
+      // Inject data into ejs template
+      injectData: {
+        title: VITE_GLOB_APP_TITLE,
+      },
+      // Embed the generated app.config.js file
+      tags: isBuild
+        ? [
+            {
+              tag: 'script',
+              attrs: {
+                src: getAppConfigSrc(),
+              },
+            },
+          ]
+        : [],
+    },
+  });
+  return htmlPlugin;
+}

+ 37 - 0
build/vite/plugin/imagemin.ts

@@ -0,0 +1,37 @@
+// Image resource files used to compress the output of the production environment
+// https://github.com/anncwb/vite-plugin-imagemin
+
+import viteImagemin from 'vite-plugin-imagemin';
+
+export function configImageminPlugin() {
+  const plugin = viteImagemin({
+    gifsicle: {
+      optimizationLevel: 7,
+      interlaced: false,
+    },
+    optipng: {
+      optimizationLevel: 7,
+    },
+    webp: {
+      quality: 75,
+    },
+    mozjpeg: {
+      quality: 65,
+    },
+    pngquant: {
+      quality: [0.65, 0.9],
+      speed: 4,
+    },
+    svgo: {
+      plugins: [
+        {
+          removeViewBox: false,
+        },
+        {
+          removeEmptyAttrs: false,
+        },
+      ],
+    },
+  });
+  return plugin;
+}

+ 67 - 0
build/vite/plugin/index.ts

@@ -0,0 +1,67 @@
+import type { Plugin } from 'vite';
+
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import legacy from '@vitejs/plugin-legacy';
+
+import PurgeIcons from 'vite-plugin-purge-icons';
+
+import { ViteEnv } from '../../utils';
+import { configHtmlPlugin } from './html';
+import { configPwaConfig } from './pwa';
+import { configMockPlugin } from './mock';
+import { configCompressPlugin } from './compress';
+import { configStyleImportPlugin } from './styleImport';
+import { configVisualizerConfig } from './visualizer';
+import { configThemePlugin } from './theme';
+import { configImageminPlugin } from './imagemin';
+import { configWindiCssPlugin } from './windicss';
+
+export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
+  const { VITE_USE_IMAGEMIN, VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS } = viteEnv;
+
+  const vitePlugins: (Plugin | Plugin[])[] = [
+    // have to
+    vue(),
+    // have to
+    vueJsx(),
+  ];
+
+  // @vitejs/plugin-legacy
+  VITE_LEGACY && isBuild && vitePlugins.push(legacy());
+
+  // vite-plugin-html
+  vitePlugins.push(configHtmlPlugin(viteEnv, isBuild));
+
+  // vite-plugin-windicss
+  vitePlugins.push(configWindiCssPlugin());
+
+  // vite-plugin-mock
+  VITE_USE_MOCK && vitePlugins.push(configMockPlugin(isBuild));
+
+  // vite-plugin-purge-icons
+  vitePlugins.push(PurgeIcons());
+
+  // vite-plugin-style-import
+  vitePlugins.push(configStyleImportPlugin());
+
+  // rollup-plugin-visualizer
+  vitePlugins.push(configVisualizerConfig());
+
+  //vite-plugin-theme
+  vitePlugins.push(configThemePlugin());
+
+  // The following plugins only work in the production environment
+  if (isBuild) {
+    //vite-plugin-imagemin
+    VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin());
+
+    // rollup-plugin-gzip
+    vitePlugins.push(configCompressPlugin(VITE_BUILD_COMPRESS));
+
+    // vite-plugin-pwa
+    vitePlugins.push(configPwaConfig(viteEnv));
+  }
+
+  return vitePlugins;
+}

+ 20 - 0
build/vite/plugin/mock.ts

@@ -0,0 +1,20 @@
+/**
+ * Mock plugin for development and production.
+ * https://github.com/anncwb/vite-plugin-mock
+ */
+import { viteMockServe } from 'vite-plugin-mock';
+
+export function configMockPlugin(isBuild: boolean) {
+  return viteMockServe({
+    ignore: /^\_/,
+    mockPath: 'mock',
+    showTime: true,
+    localEnabled: !isBuild,
+    prodEnabled: isBuild,
+    injectCode: `
+      import { setupProdMockServer } from '../mock/_createProductionServer';
+
+      setupProdMockServer();
+      `,
+  });
+}

+ 36 - 0
build/vite/plugin/pwa.ts

@@ -0,0 +1,36 @@
+/**
+ * Zero-config PWA for Vite
+ * https://github.com/antfu/vite-plugin-pwa
+ */
+
+import { VitePWA } from 'vite-plugin-pwa';
+
+import { ViteEnv } from '../../utils';
+
+export function configPwaConfig(env: ViteEnv) {
+  const { VITE_USE_PWA, VITE_GLOB_APP_TITLE, VITE_GLOB_APP_SHORT_NAME } = env;
+
+  if (VITE_USE_PWA) {
+    // vite-plugin-pwa
+    const pwaPlugin = VitePWA({
+      manifest: {
+        name: VITE_GLOB_APP_TITLE,
+        short_name: VITE_GLOB_APP_SHORT_NAME,
+        icons: [
+          {
+            src: './resource/img/pwa-192x192.png',
+            sizes: '192x192',
+            type: 'image/png',
+          },
+          {
+            src: './resource/img/pwa-512x512.png',
+            sizes: '512x512',
+            type: 'image/png',
+          },
+        ],
+      },
+    });
+    return pwaPlugin;
+  }
+  return [];
+}

+ 21 - 0
build/vite/plugin/styleImport.ts

@@ -0,0 +1,21 @@
+/**
+ *  Introduces component library styles on demand.
+ * https://github.com/anncwb/vite-plugin-style-import
+ */
+
+import styleImport from 'vite-plugin-style-import';
+
+export function configStyleImportPlugin() {
+  const pwaPlugin = styleImport({
+    libs: [
+      {
+        libraryName: 'ant-design-vue',
+        esModule: true,
+        resolveStyle: (name) => {
+          return `ant-design-vue/es/${name}/style/index`;
+        },
+      },
+    ],
+  });
+  return pwaPlugin;
+}

+ 19 - 0
build/vite/plugin/theme.ts

@@ -0,0 +1,19 @@
+/**
+ * Vite plugin for website theme color switching
+ * https://github.com/anncwb/vite-plugin-theme
+ */
+import { viteThemePlugin, mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme';
+import { getThemeColors, generateColors } from '../../config/themeConfig';
+
+export function configThemePlugin() {
+  const colors = generateColors({
+    mixDarken,
+    mixLighten,
+    tinycolor,
+  });
+
+  const plugin = viteThemePlugin({
+    colorVariables: [...getThemeColors(), ...colors],
+  });
+  return plugin;
+}

+ 15 - 0
build/vite/plugin/visualizer.ts

@@ -0,0 +1,15 @@
+/**
+ * Package file volume analysis
+ */
+import visualizer from 'rollup-plugin-visualizer';
+import { isReportMode } from '../../utils';
+
+export function configVisualizerConfig() {
+  if (isReportMode()) {
+    return visualizer({
+      filename: './node_modules/.cache/visualizer/stats.html',
+      open: true,
+    }) as Plugin;
+  }
+  return [];
+}

+ 12 - 0
build/vite/plugin/windicss.ts

@@ -0,0 +1,12 @@
+import windiCSS from 'vite-plugin-windicss';
+
+import type { Plugin } from 'vite';
+
+export function configWindiCssPlugin(): Plugin[] {
+  return windiCSS({
+    safelist: 'shadow shadow-xl',
+    preflight: {
+      enableAll: true,
+    },
+  });
+}

+ 34 - 0
build/vite/proxy.ts

@@ -0,0 +1,34 @@
+/**
+ * Used to parse the .env.development proxy configuration
+ */
+import type { ServerOptions } from 'http-proxy';
+
+type ProxyItem = [string, string];
+
+type ProxyList = ProxyItem[];
+
+type ProxyTargetList = Record<string, ServerOptions & { rewrite: (path: string) => string }>;
+
+const httpsRE = /^https:\/\//;
+
+/**
+ * Generate proxy
+ * @param list
+ */
+export function createProxy(list: ProxyList = []) {
+  const ret: ProxyTargetList = {};
+  for (const [prefix, target] of list) {
+    const isHttps = httpsRE.test(target);
+
+    // https://github.com/http-party/node-http-proxy#options
+    ret[prefix] = {
+      target: target,
+      changeOrigin: true,
+      ws: true,
+      rewrite: (path) => path.replace(new RegExp(`^${prefix}`), ''),
+      // https is require secure=false
+      ...(isHttps ? { secure: false } : {}),
+    };
+  }
+  return ret;
+}

+ 56 - 0
commitlint.config.js

@@ -0,0 +1,56 @@
+module.exports = {
+  ignores: [(commit) => commit.includes('init')],
+  extends: ['@commitlint/config-conventional'],
+  parserPreset: {
+    parserOpts: {
+      headerPattern: /^(\w*|[\u4e00-\u9fa5]*)(?:[\(\(](.*)[\)\)])?[\:\:] (.*)/,
+      headerCorrespondence: ['type', 'scope', 'subject'],
+      referenceActions: [
+        'close',
+        'closes',
+        'closed',
+        'fix',
+        'fixes',
+        'fixed',
+        'resolve',
+        'resolves',
+        'resolved',
+      ],
+      issuePrefixes: ['#'],
+      noteKeywords: ['BREAKING CHANGE', '不兼容变更'],
+      fieldPattern: /^-(.*?)-$/,
+      revertPattern: /^Revert\s"([\s\S]*)"\s*This reverts commit (\w*)\./,
+      revertCorrespondence: ['header', 'hash'],
+      warn() {},
+      mergePattern: null,
+      mergeCorrespondence: null,
+    },
+  },
+  rules: {
+    'body-leading-blank': [2, 'always'],
+    'footer-leading-blank': [1, 'always'],
+    'header-max-length': [2, 'always', 108],
+    'subject-empty': [2, 'never'],
+    'type-empty': [2, 'never'],
+    'type-enum': [
+      2,
+      'always',
+      [
+        'feat',
+        'fix',
+        'perf',
+        'style',
+        'docs',
+        'test',
+        'refactor',
+        'build',
+        'ci',
+        'chore',
+        'revert',
+        'wip',
+        'workflow',
+        'types',
+      ],
+    ],
+  },
+};

+ 0 - 0
favicon.ico


+ 148 - 0
index.html

@@ -0,0 +1,148 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+    <meta name="renderer" content="webkit" />
+    <meta
+      name="viewport"
+      content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
+    />
+
+    <title><%= title %></title>
+    <link rel="icon" href="/src/assets/images/logo.png" />
+  </head>
+  <body>
+    <div id="app">
+      <style>
+        .app-loading {
+          display: flex;
+          width: 100%;
+          height: 100%;
+          justify-content: center;
+          align-items: center;
+          flex-direction: column;
+          background: #f4f7f9;
+        }
+
+        .app-loading .app-loading-wrap {
+          position: absolute;
+          top: 50%;
+          left: 50%;
+          display: flex;
+          -webkit-transform: translate3d(-50%, -50%, 0);
+          transform: translate3d(-50%, -50%, 0);
+          justify-content: center;
+          align-items: center;
+          flex-direction: column;
+        }
+
+        .app-loading .dots {
+          display: flex;
+          padding: 98px;
+          justify-content: center;
+          align-items: center;
+        }
+
+        .app-loading .app-loading-title {
+          display: flex;
+          margin-top: 30px;
+          font-size: 30px;
+          color: rgba(0, 0, 0, 0.85);
+          justify-content: center;
+          align-items: center;
+        }
+
+        .app-loading .app-loading-logo {
+          display: block;
+          width: 90px;
+          margin: 0 auto;
+          margin-bottom: 20px;
+        }
+
+        .dot {
+          position: relative;
+          display: inline-block;
+          width: 48px;
+          height: 48px;
+          margin-top: 30px;
+          font-size: 32px;
+          transform: rotate(45deg);
+          box-sizing: border-box;
+          animation: antRotate 1.2s infinite linear;
+        }
+
+        .dot i {
+          position: absolute;
+          display: block;
+          width: 20px;
+          height: 20px;
+          background-color: #0065cc;
+          border-radius: 100%;
+          opacity: 0.3;
+          transform: scale(0.75);
+          animation: antSpinMove 1s infinite linear alternate;
+          transform-origin: 50% 50%;
+        }
+
+        .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;
+          }
+        }
+      </style>
+      <div class="app-loading">
+        <div class="app-loading-wrap">
+          <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" />
+          <div class="app-loading-dots">
+            <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
+          </div>
+          <div class="app-loading-title"><%= title %></div>
+        </div>
+      </div>
+    </div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 18 - 0
mock/_createProductionServer.ts

@@ -0,0 +1,18 @@
+import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
+
+const modules = import.meta.globEager('./**/*.ts');
+
+const mockModules: any[] = [];
+Object.keys(modules).forEach((key) => {
+  if (key.includes('/_')) {
+    return;
+  }
+  mockModules.push(...modules[key].default);
+});
+
+/**
+ * Used in a production environment. Need to manually import all modules
+ */
+export function setupProdMockServer() {
+  createProdMockServer(mockModules);
+}

+ 47 - 0
mock/_util.ts

@@ -0,0 +1,47 @@
+// Interface data format used to return a unified format
+
+export function resultSuccess<T = any>(result: T, { message = 'ok' } = {}) {
+  return {
+    code: 0,
+    result,
+    message,
+    type: 'success',
+  };
+}
+
+export function resultPageSuccess<T = any>(
+  page: number,
+  pageSize: number,
+  list: T[],
+  { message = 'ok' } = {}
+) {
+  const pageData = pagination(page, pageSize, list);
+
+  return {
+    code: 0,
+    result: {
+      items: pageData,
+      total: list.length,
+    },
+    message,
+    type: 'success',
+  };
+}
+
+export function resultError(message = 'Request failed', { code = -1, result = null } = {}) {
+  return {
+    code,
+    result,
+    message,
+    type: 'error',
+  };
+}
+
+export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]): T[] {
+  const offset = (pageNo - 1) * Number(pageSize);
+  const ret =
+    offset + Number(pageSize) >= array.length
+      ? array.slice(offset, array.length)
+      : array.slice(offset, offset + Number(pageSize));
+  return ret;
+}

+ 25 - 0
mock/demo/select-demo.ts

@@ -0,0 +1,25 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const demoList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 20; index++) {
+    result.push({
+      label: `选项${index}`,
+      value: `${index}`,
+    });
+  }
+  return result;
+})();
+
+export default [
+  {
+    url: '/api/select/getDemoOptions',
+    timeout: 4000,
+    method: 'get',
+    response: ({ query }) => {
+      console.log(query);
+      return resultSuccess(demoList);
+    },
+  },
+] as MockMethod[];

+ 151 - 0
mock/demo/system.ts

@@ -0,0 +1,151 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultPageSuccess, resultSuccess } from '../_util';
+
+const accountList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 20; index++) {
+    result.push({
+      id: `${index}`,
+      account: '@first',
+      email: '@email',
+      nickname: '@cname()',
+      role: '@first',
+      createTime: '@datetime',
+      remark: '@cword(10,20)',
+      'status|1': ['0', '1'],
+    });
+  }
+  return result;
+})();
+
+const roleList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 4; index++) {
+    result.push({
+      id: `${index}`,
+      orderNo: `${index + 1}`,
+      roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index],
+      roleValue: '@first',
+      createTime: '@datetime',
+      remark: '@cword(10,20)',
+      'status|1': ['0', '1'],
+    });
+  }
+  return result;
+})();
+
+const deptList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 3; index++) {
+    result.push({
+      id: `${index}`,
+      deptName: ['华东分部', '华南分部', '西北分部'][index],
+      orderNo: index + 1,
+      createTime: '@datetime',
+      remark: '@cword(10,20)',
+      'status|1': ['0', '0', '1'],
+      children: (() => {
+        const children: any[] = [];
+        for (let j = 0; j < 4; j++) {
+          children.push({
+            id: `${index}-${j}`,
+            deptName: ['研发部', '市场部', '商务部', '财务部'][j],
+            orderNo: j + 1,
+            createTime: '@datetime',
+            remark: '@cword(10,20)',
+            'status|1': ['0', '1'],
+            parentDept: `${index}`,
+            children: undefined,
+          });
+        }
+        return children;
+      })(),
+    });
+  }
+  return result;
+})();
+
+const menuList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 3; index++) {
+    result.push({
+      id: `${index}`,
+      icon: ['ion:layers-outline', 'ion:git-compare-outline', 'ion:tv-outline'][index],
+      component: 'LAYOUT',
+      menuName: ['Dashboard', '权限管理', '功能'][index],
+      permission: '',
+      orderNo: index + 1,
+      createTime: '@datetime',
+      'status|1': ['0', '0', '1'],
+      children: (() => {
+        const children: any[] = [];
+        for (let j = 0; j < 4; j++) {
+          children.push({
+            id: `${index}-${j}`,
+            menuName: ['菜单1', '菜单2', '菜单3', '菜单4'][j],
+            icon: 'ion:document',
+            permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index],
+            component: [
+              '/dashboard/welcome/index',
+              '/dashboard/analysis/index',
+              '/dashboard/workbench/index',
+              '/dashboard/test/index',
+            ][j],
+            orderNo: j + 1,
+            createTime: '@datetime',
+            'status|1': ['0', '1'],
+            parentMenu: `${index}`,
+            children: undefined,
+          });
+        }
+        return children;
+      })(),
+    });
+  }
+  return result;
+})();
+
+export default [
+  {
+    url: '/api/system/getAccountList',
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, accountList);
+    },
+  },
+  {
+    url: '/api/system/getRoleListByPage',
+    timeout: 100,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, roleList);
+    },
+  },
+  {
+    url: '/api/system/getAllRoleList',
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(roleList);
+    },
+  },
+  {
+    url: '/api/system/getDeptList',
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(deptList);
+    },
+  },
+  {
+    url: '/api/system/getMenuList',
+    timeout: 100,
+    method: 'get',
+    response: () => {
+      return resultSuccess(menuList);
+    },
+  },
+] as MockMethod[];

+ 38 - 0
mock/demo/table-demo.ts

@@ -0,0 +1,38 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultPageSuccess } from '../_util';
+
+const demoList = (() => {
+  const result: any[] = [];
+  for (let index = 0; index < 60; index++) {
+    result.push({
+      id: `${index}`,
+      beginTime: '@datetime',
+      endTime: '@datetime',
+      address: '@city()',
+      name: '@cname()',
+      name1: '@cname()',
+      name2: '@cname()',
+      name3: '@cname()',
+      name4: '@cname()',
+      name5: '@cname()',
+      name6: '@cname()',
+      name7: '@cname()',
+      name8: '@cname()',
+      'no|100000-10000000': 100000,
+      'status|1': ['normal', 'enable', 'disable'],
+    });
+  }
+  return result;
+})();
+
+export default [
+  {
+    url: '/api/table/getDemoList',
+    timeout: 1000,
+    method: 'get',
+    response: ({ query }) => {
+      const { page = 1, pageSize = 20 } = query;
+      return resultPageSuccess(page, pageSize, demoList);
+    },
+  },
+] as MockMethod[];

+ 280 - 0
mock/sys/menu.ts

@@ -0,0 +1,280 @@
+import { resultSuccess } from '../_util';
+import { MockMethod } from 'vite-plugin-mock';
+
+// single
+const homeRoute = {
+  path: '/home',
+  name: 'Home',
+  component: '/dashboard/welcome/index',
+  meta: {
+    title: 'routes.dashboard.welcome',
+    affix: true,
+    icon: 'bx:bx-home',
+  },
+};
+const dashboardRoute = {
+  path: '/dashboard',
+  name: 'Dashboard',
+  component: 'LAYOUT',
+  redirect: '/dashboard/workbench',
+  meta: {
+    icon: 'bx:bx-home',
+    title: 'routes.dashboard.dashboard',
+  },
+  children: [
+    {
+      path: 'workbench',
+      name: 'Workbench',
+      component: '/dashboard/workbench/index.vue',
+      meta: {
+        title: 'routes.dashboard.workbench',
+      },
+    },
+    {
+      path: 'analysis',
+      name: 'Analysis',
+      component: '/dashboard/analysis/index.vue',
+      meta: {
+        title: 'routes.dashboard.analysis',
+      },
+    },
+  ],
+};
+
+const tableRoute = {
+  path: '/table',
+  name: 'Table',
+  component: 'LAYOUT',
+  redirect: '/table/index',
+  meta: {
+    icon: 'bx:bx-table',
+    title: 'routes.table.table',
+  },
+  children: [
+    {
+      path: 'table',
+      name: 'Table',
+      component: '/table/table/index.vue',
+      meta: {
+        title: 'routes.table.table',
+        icon: 'ant-design:table-outlined',
+      },
+    },
+    {
+      path: 'edit',
+      name: 'Edit',
+      component: '/table/editTable/index.vue',
+      meta: {
+        title: 'routes.table.edit',
+        icon: 'ant-design:table-outlined',
+      },
+    },
+  ],
+};
+
+const testRoute = {
+  path: '/test',
+  name: 'TestDemo',
+  component: 'LAYOUT',
+  redirect: '/table/basic',
+  meta: {
+    icon: 'ant-design:table-outlined',
+    title: '前台测试',
+  },
+  children: [
+    {
+      path: 'basic',
+      name: 'testBasic',
+      component: '/test/index',
+      meta: {
+        title: '测试功能',
+        icon: 'bx:bx-home',
+      },
+    },
+  ],
+};
+
+const permissionRoute = {
+  path: '/permission',
+  name: 'Permission',
+  component: 'LAYOUT',
+  redirect: '/permission/role',
+  meta: {
+    icon: 'ant-design:lock-outlined',
+    title: 'routes.permission.management',
+  },
+  children: [
+    {
+      path: 'role',
+      name: 'Role',
+      component: '/permission/role/index',
+      meta: {
+        title: 'routes.permission.role',
+        icon: 'bx:bx-lock',
+      },
+    },
+  ],
+};
+
+const frontRoute = {
+  path: 'front',
+  name: 'PermissionFrontDemo',
+  meta: {
+    title: 'routes.demo.permission.front',
+  },
+  children: [
+    {
+      path: 'page',
+      name: 'FrontPageAuth',
+      component: '/demo/permission/front/index',
+      meta: {
+        title: 'routes.demo.permission.frontPage',
+      },
+    },
+    {
+      path: 'btn',
+      name: 'FrontBtnAuth',
+      component: '/demo/permission/front/Btn',
+      meta: {
+        title: 'routes.demo.permission.frontBtn',
+      },
+    },
+    {
+      path: 'auth-pageA',
+      name: 'FrontAuthPageA',
+      component: '/demo/permission/front/AuthPageA',
+      meta: {
+        title: 'routes.demo.permission.frontTestA',
+      },
+    },
+    {
+      path: 'auth-pageB',
+      name: 'FrontAuthPageB',
+      component: '/demo/permission/front/AuthPageB',
+      meta: {
+        title: 'routes.demo.permission.frontTestB',
+      },
+    },
+  ],
+};
+const backRoute = {
+  path: 'back',
+  name: 'PermissionBackDemo',
+  meta: {
+    title: 'routes.demo.permission.back',
+  },
+
+  children: [
+    {
+      path: 'page',
+      name: 'BackAuthPage',
+      component: '/demo/permission/back/index',
+      meta: {
+        title: 'routes.demo.permission.backPage',
+      },
+    },
+    {
+      path: 'btn',
+      name: 'BackAuthBtn',
+      component: '/demo/permission/back/Btn',
+      meta: {
+        title: 'routes.demo.permission.backBtn',
+      },
+    },
+  ],
+};
+const authRoute = {
+  path: '/permission',
+  name: 'Permission',
+  component: 'LAYOUT',
+  redirect: '/permission/front/page',
+  meta: {
+    icon: 'carbon:user-role',
+    title: 'routes.demo.permission.permission',
+  },
+  children: [frontRoute, backRoute],
+};
+
+const authRoute1 = {
+  path: '/permission',
+  name: 'Permission',
+  component: 'LAYOUT',
+  redirect: '/permission/front/page',
+  meta: {
+    icon: 'carbon:user-role',
+    title: 'routes.demo.permission.permission',
+  },
+  children: [backRoute],
+};
+
+const levelRoute = {
+  path: '/level',
+  name: 'Level',
+  component: 'LAYOUT',
+  redirect: '/level/menu1/menu1-1',
+  meta: {
+    icon: 'carbon:user-role',
+    title: 'routes.demo.level.level',
+  },
+
+  children: [
+    {
+      path: 'menu1',
+      name: 'Menu1Demo',
+      meta: {
+        title: 'Menu1',
+      },
+      children: [
+        {
+          path: 'menu1-1',
+          name: 'Menu11Demo',
+          meta: {
+            title: 'Menu1-1',
+          },
+          children: [
+            {
+              path: 'menu1-1-1',
+              name: 'Menu111Demo',
+              component: '/demo/level/Menu111',
+              meta: {
+                title: 'Menu111',
+              },
+            },
+          ],
+        },
+        {
+          path: 'menu1-2',
+          name: 'Menu12Demo',
+          component: '/demo/level/Menu12',
+          meta: {
+            title: 'Menu1-2',
+          },
+        },
+      ],
+    },
+    {
+      path: 'menu2',
+      name: 'Menu2Demo',
+      component: '/demo/level/Menu2',
+      meta: {
+        title: 'Menu2',
+      },
+    },
+  ],
+};
+export default [
+  {
+    url: '/api/getMenuListById',
+    timeout: 1000,
+    method: 'get',
+    response: ({ query }) => {
+      const { id } = query;
+      if (!id || id === '1') {
+        return resultSuccess([homeRoute, dashboardRoute, tableRoute, permissionRoute, testRoute]);
+      }
+      if (id === '2') {
+        return resultSuccess([dashboardRoute, authRoute1, levelRoute]);
+      }
+    },
+  },
+] as MockMethod[];

+ 107 - 0
mock/sys/user.ts

@@ -0,0 +1,107 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultError, resultSuccess } from '../_util';
+
+function createFakeUserList() {
+  return [
+    {
+      userId: '1',
+      username: 'vben',
+      nickname: '超级管理员',
+      desc: '管理员',
+      password: '123456',
+      token: 'fakeToken1',
+      roles: [
+        {
+          roleName: 'Super Admin',
+          value: 'super',
+        },
+      ],
+    },
+    {
+      userId: '2',
+      username: 'test',
+      password: '123456',
+      nickname: '测试管理员',
+      desc: '普通管理员',
+      token: 'fakeToken2',
+      roles: [
+        {
+          roleName: 'Tester',
+          value: 'test',
+        },
+      ],
+    },
+    {
+      userId: '2',
+      username: 'admin',
+      password: '123456',
+      nickname: '测试管理员',
+      desc: '普通管理员',
+      token: 'fakeToken2',
+      roles: [
+        {
+          roleName: 'Tester',
+          value: 'test',
+        },
+      ],
+    },
+  ];
+}
+
+const fakeCodeList: any = {
+  '1': ['1000', '3000', '5000'],
+
+  '2': ['2000', '4000', '6000'],
+};
+export default [
+  // mock user login
+  {
+    url: '/api/login',
+    timeout: 200,
+    method: 'post',
+    response: ({ body }) => {
+      const { username, password } = body;
+      const checkUser = createFakeUserList().find(
+        (item) => item.username === username && password === item.password
+      );
+      if (!checkUser) {
+        return resultError('帐户或密码不正确!');
+      }
+      const { userId, username: _username, token, nickname, desc, roles } = checkUser;
+      return resultSuccess({
+        roles,
+        userId,
+        username: _username,
+        token,
+        nickname,
+        desc,
+      });
+    },
+  },
+  {
+    url: '/api/getUserInfoById',
+    method: 'get',
+    response: ({ query }) => {
+      const { userId } = query;
+      const checkUser = createFakeUserList().find((item) => item.userId === userId);
+      if (!checkUser) {
+        return resultError('The corresponding user information was not obtained!');
+      }
+      return resultSuccess(checkUser);
+    },
+  },
+  {
+    url: '/api/getPermCodeByUserId',
+    timeout: 200,
+    method: 'get',
+    response: ({ query }) => {
+      const { userId } = query;
+      if (!userId) {
+        return resultError('userId is not null!');
+      }
+      const codeList = fakeCodeList[userId];
+
+      return resultSuccess(codeList);
+    },
+  },
+] as MockMethod[];

+ 130 - 0
package.json

@@ -0,0 +1,130 @@
+{
+  "name": "jt-admin",
+  "version": "2.0.1",
+  "scripts": {
+    "bootstrap": "yarn install",
+    "serve": "vite",
+    "dev": "vite",
+    "build": "vite build && esno ./build/script/postBuild.ts",
+    "build:no-cache": "yarn clean:cache && npm run build",
+    "report": "cross-env REPORT=true npm run build ",
+    "preview": "npm run build && vite preview",
+    "preview:dist": "vite preview",
+    "log": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
+    "clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
+    "clean:lib": "npx rimraf node_modules",
+    "typecheck": "vuedx-typecheck .",
+    "lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
+    "lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
+    "lint:stylelint": "stylelint --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
+    "lint:ls-lint": "ls-lint",
+    "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
+    "lint:pretty": "pretty-quick --staged",
+    "test:gzip": "http-server dist --cors --gzip -c-1",
+    "test:br": "http-server dist --cors --brotli -c-1",
+    "reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
+    "postinstall": "is-ci || husky install"
+  },
+  "dependencies": {
+    "@iconify/iconify": "^2.0.0-rc.6",
+    "@vueuse/core": "^4.1.1",
+    "@zxcvbn-ts/core": "^0.2.0",
+    "ant-design-vue": "2.0.0",
+    "apexcharts": "^3.25.0",
+    "axios": "^0.21.1",
+    "crypto-es": "^1.2.7",
+    "echarts": "^5.0.2",
+    "lodash-es": "^4.17.21",
+    "mockjs": "^1.1.0",
+    "nprogress": "^0.2.0",
+    "path-to-regexp": "^6.2.0",
+    "qrcode": "^1.4.4",
+    "sortablejs": "^1.13.0",
+    "vue": "^3.0.5",
+    "vue-i18n": "9.0.0-rc.2",
+    "vue-router": "^4.0.4",
+    "vue-types": "^3.0.2",
+    "vuex": "^4.0.0",
+    "vuex-module-decorators": "^1.0.1",
+    "xlsx": "^0.17.0"
+  },
+  "devDependencies": {
+    "@commitlint/cli": "^11.0.0",
+    "@commitlint/config-conventional": "^11.0.0",
+    "@iconify/json": "^1.1.306",
+    "@ls-lint/ls-lint": "^1.9.2",
+    "@purge-icons/generated": "^0.7.0",
+    "@types/fs-extra": "^9.0.7",
+    "@types/http-proxy": "^1.17.5",
+    "@types/lodash-es": "^4.17.4",
+    "@types/mockjs": "^1.0.3",
+    "@types/nprogress": "^0.2.0",
+    "@types/qrcode": "^1.4.0",
+    "@types/rollup-plugin-visualizer": "^2.6.0",
+    "@types/sortablejs": "^1.10.6",
+    "@types/yargs": "^16.0.0",
+    "@typescript-eslint/eslint-plugin": "^4.15.1",
+    "@typescript-eslint/parser": "^4.15.1",
+    "@vitejs/plugin-legacy": "^1.3.1",
+    "@vitejs/plugin-vue": "^1.1.4",
+    "@vitejs/plugin-vue-jsx": "^1.1.0",
+    "@vue/compiler-sfc": "^3.0.5",
+    "@vuedx/typecheck": "^0.6.3",
+    "@vuedx/typescript-plugin-vue": "^0.6.3",
+    "autoprefixer": "^10.2.4",
+    "commitizen": "^4.2.3",
+    "conventional-changelog-cli": "^2.1.1",
+    "cross-env": "^7.0.3",
+    "dotenv": "^8.2.0",
+    "eslint": "^7.20.0",
+    "eslint-config-prettier": "^7.2.0",
+    "eslint-plugin-prettier": "^3.3.1",
+    "eslint-plugin-vue": "^7.6.0",
+    "esno": "^0.4.4",
+    "fs-extra": "^9.1.0",
+    "http-server": "^0.12.3",
+    "husky": "^5.0.9",
+    "is-ci": "^3.0.0",
+    "less": "^4.1.1",
+    "lint-staged": "^10.5.4",
+    "prettier": "^2.2.1",
+    "pretty-quick": "^3.1.0",
+    "rimraf": "^3.0.2",
+    "rollup-plugin-visualizer": "^4.2.0",
+    "stylelint": "^13.11.0",
+    "stylelint-config-prettier": "^8.0.2",
+    "stylelint-config-standard": "^20.0.0",
+    "stylelint-order": "^4.1.0",
+    "ts-node": "^9.1.1",
+    "typescript": "^4.1.5",
+    "vite": "2.1.5",
+    "vite-plugin-compression": "^0.2.1",
+    "vite-plugin-html": "^2.0.0",
+    "vite-plugin-imagemin": "^0.2.7",
+    "vite-plugin-mock": "^2.1.4",
+    "vite-plugin-purge-icons": "^0.7.0",
+    "vite-plugin-pwa": "^0.5.2",
+    "vite-plugin-style-import": "^0.7.3",
+    "vite-plugin-theme": "^0.4.3",
+    "vite-plugin-windicss": "0.4.3",
+    "vue-eslint-parser": "^7.5.0",
+    "yargs": "^16.2.0"
+  },
+  "resolutions": {
+    "//": "Used to install imagemin dependencies, because imagemin may not be installed in China.If it is abroad, you can delete it",
+    "bin-wrapper": "npm:bin-wrapper-china",
+    "ecstatic": "4.1.4"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/anncwb/vue-vben-admin.git"
+  },
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/anncwb/vue-vben-admin/issues"
+  },
+  "homepage": "https://github.com/anncwb/vue-vben-admin",
+  "engines": {
+    "node": "^12 || ^14 || ^15 || ^16"
+  }
+}

+ 5 - 0
postcss.config.js

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

+ 20 - 0
prettier.config.js

@@ -0,0 +1,20 @@
+module.exports = {
+  printWidth: 100,
+  tabWidth: 2,
+  useTabs: false,
+  semi: true,
+  vueIndentScriptAndStyle: true,
+  singleQuote: true,
+  quoteProps: 'as-needed',
+  bracketSpacing: true,
+  trailingComma: 'es5',
+  jsxBracketSameLine: false,
+  jsxSingleQuote: false,
+  arrowParens: 'always',
+  insertPragma: false,
+  requirePragma: false,
+  proseWrap: 'never',
+  htmlWhitespaceSensitivity: 'strict',
+  endOfLine: 'lf',
+  rangeStart: 0,
+};

BIN
public/favicon.ico


BIN
public/resource/img/logo.png


BIN
public/resource/img/pwa-192x192.png


BIN
public/resource/img/pwa-512x512.png


+ 389 - 0
public/resource/tinymce/langs/zh_CN.js

@@ -0,0 +1,389 @@
+tinymce.addI18n('zh_CN',{
+"Redo": "\u91cd\u505a",
+"Undo": "\u64a4\u9500",
+"Cut": "\u526a\u5207",
+"Copy": "\u590d\u5236",
+"Paste": "\u7c98\u8d34",
+"Select all": "\u5168\u9009",
+"New document": "\u65b0\u6587\u4ef6",
+"Ok": "\u786e\u5b9a",
+"Cancel": "\u53d6\u6d88",
+"Visual aids": "\u7f51\u683c\u7ebf",
+"Bold": "\u7c97\u4f53",
+"Italic": "\u659c\u4f53",
+"Underline": "\u4e0b\u5212\u7ebf",
+"Strikethrough": "\u5220\u9664\u7ebf",
+"Superscript": "\u4e0a\u6807",
+"Subscript": "\u4e0b\u6807",
+"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
+"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
+"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
+"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
+"Justify": "\u4e24\u7aef\u5bf9\u9f50",
+"Bullet list": "\u9879\u76ee\u7b26\u53f7",
+"Numbered list": "\u7f16\u53f7\u5217\u8868",
+"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
+"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
+"Close": "\u5173\u95ed",
+"Formats": "\u683c\u5f0f",
+"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
+"Headers": "\u6807\u9898",
+"Header 1": "\u6807\u98981",
+"Header 2": "\u6807\u98982",
+"Header 3": "\u6807\u98983",
+"Header 4": "\u6807\u98984",
+"Header 5": "\u6807\u98985",
+"Header 6": "\u6807\u98986",
+"Headings": "\u6807\u9898",
+"Heading 1": "\u6807\u98981",
+"Heading 2": "\u6807\u98982",
+"Heading 3": "\u6807\u98983",
+"Heading 4": "\u6807\u98984",
+"Heading 5": "\u6807\u98985",
+"Heading 6": "\u6807\u98986",
+"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
+"Div": "Div",
+"Pre": "Pre",
+"Code": "\u4ee3\u7801",
+"Paragraph": "\u6bb5\u843d",
+"Blockquote": "\u5f15\u6587\u533a\u5757",
+"Inline": "\u6587\u672c",
+"Blocks": "\u57fa\u5757",
+"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
+"Fonts": "\u5b57\u4f53",
+"Font Sizes": "\u5b57\u53f7",
+"Class": "\u7c7b\u578b",
+"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
+"OR": "\u6216",
+"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
+"Upload": "\u4e0a\u4f20",
+"Block": "\u5757",
+"Align": "\u5bf9\u9f50",
+"Default": "\u9ed8\u8ba4",
+"Circle": "\u7a7a\u5fc3\u5706",
+"Disc": "\u5b9e\u5fc3\u5706",
+"Square": "\u65b9\u5757",
+"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
+"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
+"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
+"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Anchor...": "\u951a\u70b9...",
+"Name": "\u540d\u79f0",
+"Id": "\u6807\u8bc6\u7b26",
+"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
+"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
+"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
+"Special characters...": "\u7279\u6b8a\u5b57\u7b26...",
+"Source code": "\u6e90\u4ee3\u7801",
+"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
+"Language": "\u8bed\u8a00",
+"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
+"Color Picker": "\u9009\u8272\u5668",
+"R": "R",
+"G": "G",
+"B": "B",
+"Left to right": "\u4ece\u5de6\u5230\u53f3",
+"Right to left": "\u4ece\u53f3\u5230\u5de6",
+"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
+"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
+"Title": "\u6807\u9898",
+"Keywords": "\u5173\u952e\u8bcd",
+"Description": "\u63cf\u8ff0",
+"Robots": "\u673a\u5668\u4eba",
+"Author": "\u4f5c\u8005",
+"Encoding": "\u7f16\u7801",
+"Fullscreen": "\u5168\u5c4f",
+"Action": "\u64cd\u4f5c",
+"Shortcut": "\u5feb\u6377\u952e",
+"Help": "\u5e2e\u52a9",
+"Address": "\u5730\u5740",
+"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
+"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
+"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
+"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
+"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
+"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
+"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
+"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
+"Plugins": "\u63d2\u4ef6",
+"Handy Shortcuts": "\u5feb\u6377\u952e",
+"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
+"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
+"Image description": "\u56fe\u7247\u63cf\u8ff0",
+"Source": "\u5730\u5740",
+"Dimensions": "\u5927\u5c0f",
+"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
+"General": "\u666e\u901a",
+"Advanced": "\u9ad8\u7ea7",
+"Style": "\u6837\u5f0f",
+"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
+"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
+"Border": "\u8fb9\u6846",
+"Insert image": "\u63d2\u5165\u56fe\u7247",
+"Image...": "\u56fe\u7247...",
+"Image list": "\u56fe\u7247\u5217\u8868",
+"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
+"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
+"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
+"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
+"Edit image": "\u7f16\u8f91\u56fe\u7247",
+"Image options": "\u56fe\u7247\u9009\u9879",
+"Zoom in": "\u653e\u5927",
+"Zoom out": "\u7f29\u5c0f",
+"Crop": "\u88c1\u526a",
+"Resize": "\u8c03\u6574\u5927\u5c0f",
+"Orientation": "\u65b9\u5411",
+"Brightness": "\u4eae\u5ea6",
+"Sharpen": "\u9510\u5316",
+"Contrast": "\u5bf9\u6bd4\u5ea6",
+"Color levels": "\u989c\u8272\u5c42\u6b21",
+"Gamma": "\u4f3d\u9a6c\u503c",
+"Invert": "\u53cd\u8f6c",
+"Apply": "\u5e94\u7528",
+"Back": "\u540e\u9000",
+"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
+"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
+"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Text to display": "\u663e\u793a\u6587\u5b57",
+"Url": "\u5730\u5740",
+"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
+"Current window": "\u5f53\u524d\u7a97\u53e3",
+"None": "\u65e0",
+"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
+"Remove link": "\u5220\u9664\u94fe\u63a5",
+"Anchors": "\u951a\u70b9",
+"Link...": "\u94fe\u63a5...",
+"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
+"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
+"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
+"Link list": "\u94fe\u63a5\u5217\u8868",
+"Insert video": "\u63d2\u5165\u89c6\u9891",
+"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
+"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
+"Alternative source": "\u955c\u50cf",
+"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
+"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
+"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
+"Embed": "\u5185\u5d4c",
+"Media...": "\u591a\u5a92\u4f53...",
+"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
+"Page break": "\u5206\u9875\u7b26",
+"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
+"Preview": "\u9884\u89c8",
+"Print...": "\u6253\u5370...",
+"Save": "\u4fdd\u5b58",
+"Find": "\u67e5\u627e",
+"Replace with": "\u66ff\u6362\u4e3a",
+"Replace": "\u66ff\u6362",
+"Replace all": "\u5168\u90e8\u66ff\u6362",
+"Previous": "\u4e0a\u4e00\u4e2a",
+"Next": "\u4e0b\u4e00\u4e2a",
+"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
+"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
+"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
+"Find whole words only": "\u5168\u5b57\u5339\u914d",
+"Spell check": "\u62fc\u5199\u68c0\u67e5",
+"Ignore": "\u5ffd\u7565",
+"Ignore all": "\u5168\u90e8\u5ffd\u7565",
+"Finish": "\u5b8c\u6210",
+"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
+"Insert table": "\u63d2\u5165\u8868\u683c",
+"Table properties": "\u8868\u683c\u5c5e\u6027",
+"Delete table": "\u5220\u9664\u8868\u683c",
+"Cell": "\u5355\u5143\u683c",
+"Row": "\u884c",
+"Column": "\u5217",
+"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
+"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
+"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
+"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
+"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
+"Delete row": "\u5220\u9664\u884c",
+"Row properties": "\u884c\u5c5e\u6027",
+"Cut row": "\u526a\u5207\u884c",
+"Copy row": "\u590d\u5236\u884c",
+"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
+"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
+"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
+"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
+"Delete column": "\u5220\u9664\u5217",
+"Cols": "\u5217",
+"Rows": "\u884c",
+"Width": "\u5bbd",
+"Height": "\u9ad8",
+"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
+"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
+"Show caption": "\u663e\u793a\u6807\u9898",
+"Left": "\u5de6\u5bf9\u9f50",
+"Center": "\u5c45\u4e2d",
+"Right": "\u53f3\u5bf9\u9f50",
+"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
+"Scope": "\u8303\u56f4",
+"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
+"H Align": "\u6c34\u5e73\u5bf9\u9f50",
+"V Align": "\u5782\u76f4\u5bf9\u9f50",
+"Top": "\u9876\u90e8\u5bf9\u9f50",
+"Middle": "\u5782\u76f4\u5c45\u4e2d",
+"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
+"Header cell": "\u8868\u5934\u5355\u5143\u683c",
+"Row group": "\u884c\u7ec4",
+"Column group": "\u5217\u7ec4",
+"Row type": "\u884c\u7c7b\u578b",
+"Header": "\u8868\u5934",
+"Body": "\u8868\u4f53",
+"Footer": "\u8868\u5c3e",
+"Border color": "\u8fb9\u6846\u989c\u8272",
+"Insert template...": "\u63d2\u5165\u6a21\u677f...",
+"Templates": "\u6a21\u677f",
+"Template": "\u6a21\u677f",
+"Text color": "\u6587\u5b57\u989c\u8272",
+"Background color": "\u80cc\u666f\u8272",
+"Custom...": "\u81ea\u5b9a\u4e49...",
+"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
+"No color": "\u65e0",
+"Remove color": "\u79fb\u9664\u989c\u8272",
+"Table of Contents": "\u5185\u5bb9\u5217\u8868",
+"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
+"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
+"Word count": "\u5b57\u6570",
+"Words: {0}": "\u5b57\u6570\uff1a{0}",
+"{0} words": "{0} \u5b57",
+"File": "\u6587\u4ef6",
+"Edit": "\u7f16\u8f91",
+"Insert": "\u63d2\u5165",
+"View": "\u89c6\u56fe",
+"Format": "\u683c\u5f0f",
+"Table": "\u8868\u683c",
+"Tools": "\u5de5\u5177",
+"Powered by {0}": "\u7531{0}\u9a71\u52a8",
+"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
+"Image title": "\u56fe\u7247\u6807\u9898",
+"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
+"Border style": "\u8fb9\u6846\u6837\u5f0f",
+"Error": "\u9519\u8bef",
+"Warn": "\u8b66\u544a",
+"Valid": "\u6709\u6548",
+"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
+"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
+"System Font": "\u7cfb\u7edf\u5b57\u4f53",
+"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
+"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
+"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
+"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
+"example": "\u793a\u4f8b",
+"Search": "\u641c\u7d22",
+"All": "\u5168\u90e8",
+"Currency": "\u8d27\u5e01",
+"Text": "\u6587\u5b57",
+"Quotations": "\u5f15\u7528",
+"Mathematical": "\u6570\u5b66",
+"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
+"Symbols": "\u7b26\u53f7",
+"Arrows": "\u7bad\u5934",
+"User Defined": "\u81ea\u5b9a\u4e49",
+"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
+"currency sign": "\u8d27\u5e01\u7b26\u53f7",
+"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
+"colon sign": "\u5192\u53f7",
+"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
+"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
+"lira sign": "\u91cc\u62c9\u7b26\u53f7",
+"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
+"naira sign": "\u5948\u62c9\u7b26\u53f7",
+"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
+"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
+"won sign": "\u97e9\u5143\u7b26\u53f7",
+"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
+"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
+"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
+"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
+"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
+"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
+"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
+"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
+"austral sign": "\u6fb3\u5143\u7b26\u53f7",
+"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
+"cedi sign": "\u585e\u5730\u7b26\u53f7",
+"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
+"spesmilo sign": "spesmilo\u7b26\u53f7",
+"tenge sign": "\u575a\u6208\u7b26\u53f7",
+"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
+"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
+"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
+"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
+"ruble sign": "\u5362\u5e03\u7b26\u53f7",
+"yen character": "\u65e5\u5143\u5b57\u6837",
+"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
+"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
+"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
+"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
+"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
+"People": "\u4eba\u7c7b",
+"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
+"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
+"Activity": "\u6d3b\u52a8",
+"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
+"Objects": "\u7269\u4ef6",
+"Flags": "\u65d7\u5e1c",
+"Characters": "\u5b57\u7b26",
+"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
+"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
+"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
+"Update": "\u66f4\u65b0",
+"Color swatch": "\u989c\u8272\u6837\u672c",
+"Turquoise": "\u9752\u7eff\u8272",
+"Green": "\u7eff\u8272",
+"Blue": "\u84dd\u8272",
+"Purple": "\u7d2b\u8272",
+"Navy Blue": "\u6d77\u519b\u84dd",
+"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
+"Dark Green": "\u6df1\u7eff\u8272",
+"Medium Blue": "\u4e2d\u84dd\u8272",
+"Medium Purple": "\u4e2d\u7d2b\u8272",
+"Midnight Blue": "\u6df1\u84dd\u8272",
+"Yellow": "\u9ec4\u8272",
+"Orange": "\u6a59\u8272",
+"Red": "\u7ea2\u8272",
+"Light Gray": "\u6d45\u7070\u8272",
+"Gray": "\u7070\u8272",
+"Dark Yellow": "\u6697\u9ec4\u8272",
+"Dark Orange": "\u6df1\u6a59\u8272",
+"Dark Red": "\u6df1\u7ea2\u8272",
+"Medium Gray": "\u4e2d\u7070\u8272",
+"Dark Gray": "\u6df1\u7070\u8272",
+"Black": "\u9ed1\u8272",
+"White": "\u767d\u8272",
+"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
+"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
+"history": "\u5386\u53f2",
+"styles": "\u6837\u5f0f",
+"formatting": "\u683c\u5f0f\u5316",
+"alignment": "\u5bf9\u9f50",
+"indentation": "\u7f29\u8fdb",
+"permanent pen": "\u8bb0\u53f7\u7b14",
+"comments": "\u5907\u6ce8",
+"Anchor": "\u951a\u70b9",
+"Special character": "\u7279\u6b8a\u7b26\u53f7",
+"Code sample": "\u4ee3\u7801\u793a\u4f8b",
+"Color": "\u989c\u8272",
+"Emoticons": "\u8868\u60c5",
+"Document properties": "\u6587\u6863\u5c5e\u6027",
+"Image": "\u56fe\u7247",
+"Insert link": "\u63d2\u5165\u94fe\u63a5",
+"Target": "\u6253\u5f00\u65b9\u5f0f",
+"Link": "\u94fe\u63a5",
+"Poster": "\u5c01\u9762",
+"Media": "\u5a92\u4f53",
+"Print": "\u6253\u5370",
+"Prev": "\u4e0a\u4e00\u4e2a",
+"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
+"Whole words": "\u5168\u5b57\u5339\u914d",
+"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
+"Caption": "\u6807\u9898",
+"Insert template": "\u63d2\u5165\u6a21\u677f"
+});

+ 38 - 0
src/App.vue

@@ -0,0 +1,38 @@
+<template>
+  <ConfigProvider v-bind="lockEvent" :locale="antConfigLocale">
+    <AppProvider>
+      <RouterView />
+    </AppProvider>
+  </ConfigProvider>
+</template>
+
+<script lang="ts">
+  import { defineComponent } from 'vue';
+  import { ConfigProvider } from 'ant-design-vue';
+  import { AppProvider } from '/@/components/Application';
+
+  import { initAppConfigStore } from '/@/logics/initAppConfig';
+
+  import { useLockPage } from '/@/hooks/web/useLockPage';
+  import { useLocale } from '/@/locales/useLocale';
+
+  export default defineComponent({
+    name: 'App',
+    components: { ConfigProvider, AppProvider },
+    setup() {
+      // support Multi-language
+      const { antConfigLocale, setLocale } = useLocale();
+      setLocale();
+
+      // Initialize vuex internal system configuration
+      initAppConfigStore();
+      // Create a lock screen monitor
+      const lockEvent = useLockPage();
+
+      return {
+        antConfigLocale,
+        lockEvent,
+      };
+    },
+  });
+</script>

+ 11 - 0
src/api/demo/model/optionsModel.ts

@@ -0,0 +1,11 @@
+import { BasicFetchResult } from '/@/api/model/baseModel';
+
+export interface DemoOptionsItem {
+  label: string;
+  value: string;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type DemoOptionsGetResultModel = BasicFetchResult<DemoOptionsItem[]>;

+ 74 - 0
src/api/demo/model/systemModel.ts

@@ -0,0 +1,74 @@
+import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
+
+export type AccountParams = BasicPageParams & {
+  account?: string;
+  nickname?: string;
+};
+
+export type RoleParams = {
+  roleName?: string;
+  status?: string;
+};
+
+export type RolePageParams = BasicPageParams & RoleParams;
+
+export type DeptParams = {
+  deptName?: string;
+  status?: string;
+};
+
+export type MenuParams = {
+  menuName?: string;
+  status?: string;
+};
+
+export interface AccountListItem {
+  id: string;
+  account: string;
+  email: string;
+  nickname: string;
+  role: number;
+  createTime: string;
+  remark: string;
+  status: number;
+}
+
+export interface DeptListItem {
+  id: string;
+  orderNo: string;
+  createTime: string;
+  remark: string;
+  status: number;
+}
+
+export interface MenuListItem {
+  id: string;
+  orderNo: string;
+  createTime: string;
+  status: number;
+  icon: string;
+  component: string;
+  permission: string;
+}
+
+export interface RoleListItem {
+  id: string;
+  roleName: string;
+  roleValue: string;
+  status: number;
+  orderNo: string;
+  createTime: string;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type AccountListGetResultModel = BasicFetchResult<AccountListItem>;
+
+export type DeptListGetResultModel = BasicFetchResult<DeptListItem>;
+
+export type MenuListGetResultModel = BasicFetchResult<MenuListItem>;
+
+export type RolePageListGetResultModel = BasicFetchResult<RoleListItem>;
+
+export type RoleListGetResultModel = RoleListItem[];

+ 20 - 0
src/api/demo/model/tableModel.ts

@@ -0,0 +1,20 @@
+import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
+/**
+ * @description: Request list interface parameters
+ */
+export type DemoParams = BasicPageParams;
+
+export interface DemoListItem {
+  id: string;
+  beginTime: string;
+  endTime: string;
+  address: string;
+  name: string;
+  no: number;
+  status: number;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type DemoListGetResultModel = BasicFetchResult<DemoListItem>;

+ 16 - 0
src/api/demo/select.ts

@@ -0,0 +1,16 @@
+import { defHttp } from '/@/utils/http/axios';
+import { DemoOptionsGetResultModel } from './model/optionsModel';
+
+enum Api {
+  OPTIONS_LIST = '/select/getDemoOptions',
+}
+
+/**
+ * @description: Get sample options value
+ */
+export function optionsListApi() {
+  return defHttp.request<DemoOptionsGetResultModel>({
+    url: Api.OPTIONS_LIST,
+    method: 'GET',
+  });
+}

+ 36 - 0
src/api/demo/system.ts

@@ -0,0 +1,36 @@
+import {
+  AccountParams,
+  DeptListItem,
+  MenuParams,
+  RoleParams,
+  RolePageParams,
+  MenuListGetResultModel,
+  DeptListGetResultModel,
+  AccountListGetResultModel,
+  RolePageListGetResultModel,
+  RoleListGetResultModel,
+} from './model/systemModel';
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  AccountList = '/system/getAccountList',
+  DeptList = '/system/getDeptList',
+  MenuList = '/system/getMenuList',
+  RolePageList = '/system/getRoleListByPage',
+  GetAllRoleList = '/system/getAllRoleList',
+}
+
+export const getAccountList = (params: AccountParams) =>
+  defHttp.get<AccountListGetResultModel>({ url: Api.AccountList, params });
+
+export const getDeptList = (params?: DeptListItem) =>
+  defHttp.get<DeptListGetResultModel>({ url: Api.DeptList, params });
+
+export const getMenuList = (params?: MenuParams) =>
+  defHttp.get<MenuListGetResultModel>({ url: Api.MenuList, params });
+
+export const getRoleListByPage = (params?: RolePageParams) =>
+  defHttp.get<RolePageListGetResultModel>({ url: Api.RolePageList, params });
+
+export const getAllRoleList = (params?: RoleParams) =>
+  defHttp.get<RoleListGetResultModel>({ url: Api.GetAllRoleList, params });

+ 20 - 0
src/api/demo/table.ts

@@ -0,0 +1,20 @@
+import { defHttp } from '/@/utils/http/axios';
+import { DemoParams, DemoListGetResultModel } from './model/tableModel';
+
+enum Api {
+  DEMO_LIST = '/table/getDemoList',
+}
+
+/**
+ * @description: Get sample list value
+ */
+export function demoListApi(params: DemoParams) {
+  return defHttp.request<DemoListGetResultModel>({
+    url: Api.DEMO_LIST,
+    method: 'GET',
+    params,
+    headers: {
+      ignoreCancelToken: true,
+    },
+  });
+}

+ 9 - 0
src/api/model/baseModel.ts

@@ -0,0 +1,9 @@
+export interface BasicPageParams {
+  page: number;
+  pageSize: number;
+}
+
+export interface BasicFetchResult<T extends any> {
+  items: T;
+  total: number;
+}

+ 68 - 0
src/api/sys/menu.ts

@@ -0,0 +1,68 @@
+import { defHttp } from '/@/utils/http/axios';
+
+import {
+  getMenuListByIdParams,
+  getAllMenuListResultModel,
+  getMenuListByIdParamsResultModel,
+  addMenuParams,
+  editMenuParams,
+  deleteMenuParams,
+} from './model/menuModel';
+
+enum Api {
+  GetMenuListById = '/getMenuListById',
+  GetAllMenuList = '/getAllMenuList',
+  AddMenu = '/addMenu',
+  EditMenu = '/editMenu',
+  DeleteMenu = '/deleteMenu',
+}
+
+/**
+ * @description: Get user menu based on id
+ */
+export function getMenuListById(params: getMenuListByIdParams) {
+  return defHttp.request<getMenuListByIdParamsResultModel>({
+    url: Api.GetMenuListById,
+    method: 'POST',
+    params,
+  });
+}
+/**
+ * @description: Get user menu based on id
+ */
+export function addMenu(params: addMenuParams) {
+  return defHttp.request({
+    url: Api.AddMenu,
+    method: 'POST',
+    params,
+  });
+}
+/**
+ * @description: Get user menu based on id
+ */
+export function editMenu(params: editMenuParams) {
+  return defHttp.request({
+    url: Api.EditMenu,
+    method: 'POST',
+    params,
+  });
+}
+/**
+ * @description: Get user menu based on id
+ */
+export function deleteMenu(params: deleteMenuParams) {
+  return defHttp.request({
+    url: Api.DeleteMenu,
+    method: 'POST',
+    params,
+  });
+}
+/**
+ * @description: Get all based on id
+ */
+export function getAllMenuList() {
+  return defHttp.request<getAllMenuListResultModel>({
+    url: Api.GetAllMenuList,
+    method: 'GET',
+  });
+}

+ 72 - 0
src/api/sys/model/menuModel.ts

@@ -0,0 +1,72 @@
+import { RouteMeta } from '/@/router/types';
+export interface RouteItem {
+  path: string;
+  component: any;
+  meta: RouteMeta;
+  name?: string;
+  alias?: string | string[];
+  redirect?: string;
+  caseSensitive?: boolean;
+  children?: RouteItem[];
+}
+
+/**
+ * @description: Get menu interface
+ */
+export interface getMenuListByIdParams {
+  id: number | string;
+}
+/**
+ * @description: Delete menu interface
+ */
+export interface deleteMenuParams {
+  id: number | string;
+}
+/**
+ * @description: Add menu interface
+ */
+export interface addMenuParams {
+  path: string;
+  name?: string;
+  component: any;
+  icon?: string;
+  parent?: number;
+  menuName?: string;
+  redirect?: string;
+  status?: boolean;
+}
+/**
+ * @description: Edit menu interface
+ */
+export interface editMenuParams {
+  id: number | number;
+  path: string;
+  name?: string;
+  component: any;
+  icon?: string;
+  parent?: number;
+  menuName?: string;
+  redirect?: string;
+  status?: boolean;
+}
+
+/**
+ * @description: Get menu return value
+ */
+export type getMenuListByIdParamsResultModel = RouteItem[];
+
+export interface getAllMenuListResultModel {
+  id: number;
+  path: string;
+  name?: string;
+  component: any;
+  icon?: string;
+  parent?: number;
+  create_time?: Date;
+  menuName?: string;
+  // alias?: string | string[];
+  redirect?: string;
+  status?: boolean;
+  caseSensitive?: boolean;
+  children?: getAllMenuListResultModel[];
+}

+ 5 - 0
src/api/sys/model/uploadModel.ts

@@ -0,0 +1,5 @@
+export interface UploadApiResult {
+  message: string;
+  code: number;
+  url: string;
+}

+ 149 - 0
src/api/sys/model/userModel.ts

@@ -0,0 +1,149 @@
+/**
+ * @description: Login interface parameters
+ */
+export interface LoginParams {
+  username: string;
+  password: string;
+}
+
+/**
+ * @description: Get user information
+ */
+export interface GetUserInfoByUserIdParams {
+  id: string | number;
+}
+
+export interface getUserListParams {
+  limit?: number;
+  offset?: number;
+  order?: string;
+  sort?: string;
+}
+
+export interface AddUserParams {
+  username: string;
+  password: string;
+  // 真实名字
+  nickname: string;
+
+  status?: boolean;
+  // 介绍
+  detail?: string;
+  menus?: string[] | number[];
+}
+export interface EditUserParams {
+  id: number;
+  username: string;
+  password: string;
+  // 真实名字
+  nickname: string;
+
+  status?: boolean;
+  // 介绍
+  detail?: string;
+  menus?: string[] | number[];
+}
+
+export interface DeleteUserParams {
+  id: number;
+}
+
+export interface RoleInfo {
+  roleName: string;
+  value: string;
+}
+
+/**
+ * @description: Login interface return value
+ */
+export interface LoginResultModel {
+  id: string | number;
+  token: string;
+  role: RoleInfo;
+}
+
+/**
+ * @description: Get common information return value
+ */
+export interface CommonRowModel {
+  count: number;
+  row: object[];
+}
+/**
+ * @description: Get user information return value
+ */
+export interface CommonTreeModel {
+  count: number;
+  tree: object[];
+}
+/**
+ * @description: Get GroupTree information return value
+ */
+export interface getGroupTreeModel {
+  count: number;
+}
+/**
+ * @description: Get user information return value
+ */
+export interface GetUserInfoByUserIdModel {
+  // 用户id
+  id: string | number;
+  // 用户名
+  username: string;
+  nickname: string;
+  email: string;
+  logintime: number;
+  row: any;
+}
+
+export interface AddGroupParams {
+  pid: string | number;
+  name: string;
+  rules: string;
+  status: string;
+}
+
+export interface EditGroupParams {
+  id: number;
+  pid?: string | number;
+  name?: string;
+  rules?: string;
+  status?: string;
+}
+export interface GroupIdParams {
+  id: number;
+}
+
+export interface AddRuleParams {
+  condition: string;
+  icon: string;
+  ismenu: boolean;
+  name: string;
+  pid?: number;
+  remark?: string;
+  status: string;
+  title: string;
+  type: string;
+  createtime?: number | string;
+  updatetime?: number | string;
+  weigh: number;
+}
+
+export interface EditRuleParams {
+  id: number;
+  condition: string;
+  icon: string;
+  ismenu: boolean;
+  name: string;
+  pid?: number;
+  remark?: string;
+  status: string;
+  title: string;
+  type: string;
+  createtime?: number | string;
+  updatetime?: number | string;
+  weigh: number;
+}
+export interface RuleIdParams {
+  id: number;
+}

+ 27 - 0
src/api/sys/upload.ts

@@ -0,0 +1,27 @@
+import { UploadApiResult } from './model/uploadModel';
+import { defHttp } from '/@/utils/http/axios';
+import { UploadFileParams } from '/@/utils/http/axios/types';
+import { useGlobSetting } from '/@/hooks/setting';
+
+const { uploadUrl = '' } = useGlobSetting();
+
+/**
+ * @description: Upload interface
+ */
+export function uploadApi(
+  params: UploadFileParams,
+  onUploadProgress: (progressEvent: ProgressEvent) => void
+) {
+  console.log('====================');
+  console.log(params);
+  console.log(uploadUrl);
+  console.log('uploadUrl');
+  console.log('====================');
+  return defHttp.uploadFile<UploadApiResult>(
+    {
+      url: uploadUrl,
+      onUploadProgress,
+    },
+    params
+  );
+}

+ 161 - 0
src/api/sys/user.ts

@@ -0,0 +1,161 @@
+import { defHttp } from '/@/utils/http/axios';
+import {
+  LoginParams,
+  LoginResultModel,
+  GetUserInfoByUserIdParams,
+  getUserListParams,
+  GetUserInfoByUserIdModel,
+  CommonRowModel,
+  CommonTreeModel,
+  AddUserParams,
+  DeleteUserParams,
+  EditUserParams,
+  AddGroupParams,
+  EditGroupParams,
+  GroupIdParams,
+  AddRuleParams,
+  EditRuleParams,
+  RuleIdParams,
+} from './model/userModel';
+import { ErrorMessageMode } from '/@/utils/http/axios/types';
+
+enum Api {
+  LoginUrl = '/login',
+  UserById = '/',
+  AdminUrl = '/',
+  GroupUrl = '/group',
+  GroupById = '/group/',
+  RuleUrl = '/rule',
+  RuleById = '/rule/',
+  allowUrl = '/allow/', // 允许访问的规则 <pid>
+}
+
+/**
+ * @description: user login api
+ */
+export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') {
+  return defHttp.request<LoginResultModel>(
+    {
+      url: Api.LoginUrl,
+      method: 'POST',
+      params,
+    },
+    {
+      errorMessageMode: mode,
+    }
+  );
+}
+
+/**
+ * @description: getUserInfoById
+ */
+export function getUserInfoById(params: GetUserInfoByUserIdParams) {
+  return defHttp.request<GetUserInfoByUserIdModel>({
+    url: Api.UserById + params.id,
+    method: 'GET',
+  });
+}
+// 管理员列表
+export function getUserList(params?: getUserListParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.AdminUrl,
+    method: 'GET',
+    params,
+  });
+}
+//添加管理员 用户
+export function addUser(params: AddUserParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.AdminUrl,
+    method: 'POST',
+    params,
+  });
+}
+export function editUser(params: EditUserParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.UserById + params.id,
+    method: 'POST',
+    params,
+  });
+}
+export function deleteUser(params: DeleteUserParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.UserById + params.id,
+    method: 'DELETE',
+  });
+}
+
+// ===================角色组======================
+export function getGroupTree() {
+  return defHttp.request<CommonTreeModel>({
+    url: Api.GroupUrl,
+    method: 'GET',
+  });
+}
+export function addGroup(params: AddGroupParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.GroupUrl,
+    method: 'POST',
+    params,
+  });
+}
+export function editGroup(params: EditGroupParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.GroupById + params.id,
+    method: 'POST',
+    params,
+  });
+}
+export function deleteGroup(params: GroupIdParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.GroupById + params.id,
+    method: 'DELETE',
+  });
+}
+export function getGroupById(params: GroupIdParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.GroupById + params.id,
+    method: 'GET',
+  });
+}
+
+// =====================菜单规则===========
+
+export function getRuleTree() {
+  return defHttp.request<CommonTreeModel>({
+    url: Api.RuleUrl,
+    method: 'GET',
+  });
+}
+export function addRule(params: AddRuleParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.RuleUrl,
+    method: 'POST',
+    params,
+  });
+}
+export function editRule(params: EditRuleParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.RuleById + params.id,
+    method: 'POST',
+    params,
+  });
+}
+export function deleteRule(params: RuleIdParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.RuleById + params.id,
+    method: 'DELETE',
+  });
+}
+export function getRuleById(params: RuleIdParams) {
+  return defHttp.request<CommonRowModel>({
+    url: Api.RuleById + params.id,
+    method: 'GET',
+  });
+}
+export function getAllowRule(params: RuleIdParams) {
+  return defHttp.request<CommonTreeModel>({
+    url: Api.allowUrl + params.id,
+    method: 'GET',
+  });
+}

BIN
src/assets/images/dashboard/wokb/approve.png


BIN
src/assets/images/dashboard/wokb/attendance.png


BIN
src/assets/images/dashboard/wokb/datashow1.png


BIN
src/assets/images/dashboard/wokb/datashow2.png


BIN
src/assets/images/dashboard/wokb/datashow3.png


BIN
src/assets/images/dashboard/wokb/datashow4.png


BIN
src/assets/images/dashboard/wokb/leave.png


BIN
src/assets/images/dashboard/wokb/meal.png


BIN
src/assets/images/dashboard/wokb/overtime.png


BIN
src/assets/images/dashboard/wokb/performance.png


BIN
src/assets/images/dashboard/wokb/stamp.png


BIN
src/assets/images/dashboard/wokb/travel.png


BIN
src/assets/images/dashboard/wokb/wokb.png


BIN
src/assets/images/demo.png


BIN
src/assets/images/header.jpg


BIN
src/assets/images/logo.png


+ 20 - 0
src/assets/svg/dashboard/analysis-down.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="20px" height="12px" viewBox="0 0 20 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 61 (89581) - https://sketch.com -->
+    <title>下跌-24px</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="系统首页" transform="translate(-850.000000, -241.000000)">
+            <g id="1" transform="translate(234.000000, 120.000000)">
+                <g id="Total-Sales" transform="translate(598.000000, 0.000000)">
+                    <g id="8.5%-Up-from-yesterday" transform="translate(16.000000, 114.000000)">
+                        <g id="下跌-24px" transform="translate(0.000000, 1.000000)">
+                            <polygon id="Path" points="0 0 24 0 24 24 0 24"></polygon>
+                            <polygon id="Path" fill="#ED6F6F" fill-rule="nonzero" points="16 18 18.29 15.71 13.41 10.83 9.41 14.83 2 7.41 3.41 6 9.41 12 13.41 8 19.71 14.29 22 12 22 18"></polygon>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 21 - 0
src/assets/svg/dashboard/analysis-icon1.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="60px" height="60px" viewBox="0 0 60 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 61 (89581) - https://sketch.com -->
+    <title>Icon1@3x</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="系统首页" transform="translate(-419.000000, -136.000000)" fill="#0593FF">
+            <g id="1" transform="translate(234.000000, 120.000000)">
+                <g id="Total-Users">
+                    <g id="Icon1" transform="translate(185.000000, 16.000000)">
+                        <path d="M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z" id="Circle-2" opacity="0.209999993"></path>
+                        <g id="Group" transform="translate(14.000000, 18.000000)" fill-rule="nonzero">
+                            <path d="M24,6.66666667 C26.209139,6.66666667 28,8.45752767 28,10.6666667 C28,12.8758057 26.209139,14.6666667 24,14.6666667 C21.790861,14.6666667 20,12.8758057 20,10.6666667 C20,8.45752767 21.790861,6.66666667 24,6.66666667 Z M12,0 C14.9455187,0 17.3333333,2.38781467 17.3333333,5.33333333 C17.3333333,8.278852 14.9455187,10.6666667 12,10.6666667 C9.05448133,10.6666667 6.66666667,8.278852 6.66666667,5.33333333 C6.66666667,2.38781467 9.05448133,0 12,0 Z" id="Combined-Shape" opacity="0.587820871"></path>
+                            <path d="M23.4686027,16.0012776 L23.3172917,16 C27.927838,16 31.7158139,18.2931929 31.9979916,23.2 C32.0092328,23.3954741 31.9979916,24 31.2745999,24 L26.1333333,24 L26.1333333,24 C26.1333333,20.9989578 25.1418595,18.2294867 23.4686027,16.0012776 Z M11.9777884,13.3333333 C18.3616218,13.3333333 23.6065116,16.3909238 23.9972191,22.9333333 C24.0127839,23.1939654 23.9972191,24 22.9955999,24 L0.97000297,24 L0.97000297,24 C0.635616207,24 -0.027282334,23.2789066 0.000868912387,22.932274 C0.517678033,16.5686878 5.6825498,13.3333333 11.9777884,13.3333333 Z" id="Combined-Shape"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 21 - 0
src/assets/svg/dashboard/analysis-icon2.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="60px" height="60px" viewBox="0 0 60 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 61 (89581) - https://sketch.com -->
+    <title>Icon2@3x</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="系统首页" transform="translate(-719.000000, -136.000000)">
+            <g id="1" transform="translate(234.000000, 120.000000)">
+                <g id="Total-Order" transform="translate(299.000000, 0.000000)">
+                    <g id="Icon2" transform="translate(186.000000, 16.000000)">
+                        <path d="M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z" id="Circle-2" fill="#FFD164" opacity="0.209999993"></path>
+                        <g id="icon" transform="translate(15.000000, 13.000000)">
+                            <path d="M0,11.3164701 L12.9004912,18.7645722 C13.0394036,18.8447733 13.1850623,18.9027046 13.3333333,18.9394739 L13.3333333,33.3847054 L0.920064885,26.0385088 C0.349783865,25.7010154 0,25.0875659 0,24.4249029 L0,11.3164701 Z M30,11.1184665 L30,24.4249029 C30,25.0875659 29.6502161,25.7010154 29.0799351,26.0385088 L16.6666667,33.3847054 L16.6666667,18.8129235 C16.6969108,18.7978151 16.7268876,18.7817016 16.7565565,18.7645722 L30,11.1184665 L30,11.1184665 Z" id="Combined-Shape" fill="#FFC741"></path>
+                            <path d="M0.405221909,7.70142332 C0.562796988,7.50243849 0.761684783,7.33426405 0.993563997,7.21076013 L14.118564,0.220099528 C14.6695479,-0.0733665093 15.3304521,-0.0733665093 15.881436,0.220099528 L29.006436,7.21076013 C29.1851826,7.30596446 29.3443248,7.42771319 29.480051,7.56965747 L15.0898899,15.8778209 C14.9952678,15.9324509 14.9080291,15.9949583 14.8285239,16.0640363 C14.7490186,15.9949583 14.66178,15.9324509 14.5671579,15.8778209 L0.405221909,7.70142332 Z" id="Path" fill="#FFD164" opacity="0.659481957"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 21 - 0
src/assets/svg/dashboard/analysis-icon3.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="60px" height="60px" viewBox="0 0 60 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 61 (89581) - https://sketch.com -->
+    <title>Icon3@3x</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="系统首页" transform="translate(-1018.000000, -136.000000)" fill="#55D187">
+            <g id="1" transform="translate(234.000000, 120.000000)">
+                <g id="Total-Sales" transform="translate(598.000000, 0.000000)">
+                    <g id="Icon3" transform="translate(186.000000, 16.000000)">
+                        <path d="M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z" id="Circle-2" opacity="0.209999993"></path>
+                        <g id="icon" transform="translate(16.000000, 16.000000)" fill-rule="nonzero">
+                            <path d="M3.11111111,24.8888889 L26.4444444,24.8888889 C27.3035541,24.8888889 28,25.5853348 28,26.4444444 C28,27.3035541 27.3035541,28 26.4444444,28 L1.55555556,28 C0.696445945,28 0,27.3035541 0,26.4444444 L0,1.55555556 C0,0.696445945 0.696445945,0 1.55555556,0 C2.41466517,0 3.11111111,0.696445945 3.11111111,1.55555556 L3.11111111,24.8888889 Z" id="Path-95"></path>
+                            <path d="M8.91261343,18.1750195 C8.32503303,18.801772 7.34062178,18.8335272 6.71386936,18.2459468 C6.08711693,17.6583664 6.05536173,16.6739551 6.64294213,16.0472027 L12.4762755,9.82498047 C13.044535,9.21883699 13.9888279,9.16627114 14.6208522,9.70559855 L19.2248856,13.6343737 L25.2235157,6.03610888 C25.7558581,5.36180856 26.7340352,5.24672889 27.4083356,5.77907125 C28.0826359,6.31141362 28.1977156,7.28959079 27.6653732,7.96389112 L20.6653732,16.8305578 C20.118618,17.5231144 19.1059101,17.6227201 18.4347034,17.049957 L13.7306235,13.0358088 L8.91261343,18.1750195 Z" id="Path-97" opacity="0.657133557"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 21 - 0
src/assets/svg/dashboard/analysis-icon4.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="60px" height="60px" viewBox="0 0 60 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 61 (89581) - https://sketch.com -->
+    <title>Icon</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="系统首页" transform="translate(-1317.000000, -136.000000)" fill="#FF9066">
+            <g id="1" transform="translate(234.000000, 120.000000)">
+                <g id="Order-Pending" transform="translate(897.000000, 0.000000)">
+                    <g id="Icon" transform="translate(186.000000, 16.000000)">
+                        <path d="M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z" id="Circle-2" opacity="0.3"></path>
+                        <g id="icon" transform="translate(16.000000, 15.000000)">
+                            <path d="M13.1296822,8.34718934 L13.5475062,8.34718934 C13.8043819,8.34718934 14.0194647,8.54183658 14.0450248,8.79743748 L14.6666667,15.013856 L14.6666667,15.013856 L19.0814028,17.5365624 C19.2371903,17.6255838 19.3333333,17.7912555 19.3333333,17.9706839 L19.3333333,18.3592308 C19.3333333,18.6353732 19.1094757,18.8592308 18.8333333,18.8592308 C18.7888923,18.8592308 18.7446497,18.8533059 18.7017746,18.8416127 L12.3986612,17.1225818 C12.1672824,17.0594785 12.0132986,16.8409746 12.0316926,16.6018516 L12.631155,8.80884109 C12.6511933,8.54834251 12.8684141,8.34718934 13.1296822,8.34718934 Z" id="Path-107" opacity="0.779999971"></path>
+                            <path d="M6.01733907,-0.0772351689 C6.2288764,-0.254736066 6.5442542,-0.227144084 6.72175509,-0.0156067521 L6.72175509,-0.0156067521 L8.51913552,2.1273858 C10.2024553,1.41052645 12.054904,1.01385601 14,1.01385601 C21.7319865,1.01385601 28,7.28186951 28,15.013856 C28,22.6413562 21.9002476,28.8441829 14.312645,29.010434 L14,29.013856 L14,29.013856 C6.2680135,29.013856 0,22.7458425 0,15.013856 C0,13.7006992 0.180792724,12.4297687 0.518844853,11.224598 L3.08641434,11.944805 C2.80896017,12.9339413 2.66666667,13.9630912 2.66666667,15.013856 C2.66666667,21.2730832 7.74077284,26.3471893 14,26.3471893 C20.2592272,26.3471893 25.3333333,21.2730832 25.3333333,15.013856 C25.3333333,8.85242927 20.4165542,3.83937783 14.2925184,3.68422422 L14,3.68052267 L14,3.68052267 C12.7318949,3.68052267 11.4968995,3.88835566 10.3322213,4.2862714 L12.1330448,6.43331723 C12.2023675,6.51593278 12.24312,6.61874811 12.2492217,6.7264223 C12.2648452,7.00212236 12.0540114,7.23828671 11.7783113,7.25391015 L11.7783113,7.25391015 L4.73355552,7.65312407 C4.68508783,7.65587065 4.6364785,7.65154413 4.58925778,7.64028071 C4.32065092,7.57621071 4.15484104,7.30652283 4.21891104,7.03791597 L4.21891104,7.03791597 L5.85237713,0.189778054 C5.87728008,0.0853749765 5.93511798,-0.00824348388 6.01733907,-0.0772351689 Z" id="Combined-Shape" opacity="0.901274182"></path>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 20 - 0
src/assets/svg/dashboard/analysis-rise.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="20px" height="12px" viewBox="0 0 20 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 61 (89581) - https://sketch.com -->
+    <title>上涨-24px</title>
+    <desc>Created with Sketch.</desc>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="系统首页" transform="translate(-551.000000, -240.000000)">
+            <g id="1" transform="translate(234.000000, 120.000000)">
+                <g id="Total-Order" transform="translate(299.000000, 0.000000)">
+                    <g id="8.5%-Up-from-yesterday" transform="translate(16.000000, 114.000000)">
+                        <g id="上涨-24px">
+                            <polygon id="Path" points="0 0 24 0 24 24 0 24"></polygon>
+                            <polygon id="Path" fill="#55D187" fill-rule="nonzero" points="16 6 18.29 8.29 13.41 13.17 9.41 9.17 2 16.59 3.41 18 9.41 12 13.41 16 19.71 9.71 22 12 22 6"></polygon>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 17 - 0
src/assets/svg/login-bg.svg

@@ -0,0 +1,17 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6395" height="1079" viewBox="0 0 6395 1079">
+  <defs>
+    <clipPath id="clip-path">
+      <rect id="Rectangle_73" data-name="Rectangle 73" width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
+    </clipPath>
+    <linearGradient id="linear-gradient" x1="0.747" y1="0.222" x2="0.973" y2="0.807" gradientUnits="objectBoundingBox">
+      <stop offset="0" stop-color="#2b51b4"/>
+      <stop offset="1" stop-color="#1c3faa"/>
+    </linearGradient>
+  </defs>
+  <g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
+    <g id="Group_118" data-name="Group 118" transform="translate(-419.333 -1.126)">
+      <path id="Path_142" data-name="Path 142" d="M6271.734-6.176s-222.478,187.809-55.349,583.254c44.957,106.375,81.514,205.964,84.521,277,8.164,192.764-156.046,268.564-156.046,268.564l-653.53-26.8L5475.065-21.625Z" transform="translate(-4876.383 0)" fill="#f1f5f8"/>
+      <path id="Union_6" data-name="Union 6" d="M-2631.1,1081.8v-1.6H-8230.9V.022H-2631.1V0H-1871.4s-187.845,197.448-91.626,488.844c49.167,148.9,96.309,256.289,104.683,362.118,7.979,100.852-57.98,201.711-168.644,254.286-65.858,31.29-144.552,42.382-223.028,42.383C-2441.2,1147.632-2631.1,1081.8-2631.1,1081.8Z" transform="translate(3259.524 0.803)" fill="url(#linear-gradient)"/>
+    </g>
+  </g>
+</svg>

File diff suppressed because it is too large
+ 0 - 0
src/assets/svg/login-box-bg.svg


File diff suppressed because it is too large
+ 0 - 0
src/assets/svg/net-error.svg


File diff suppressed because it is too large
+ 0 - 0
src/assets/svg/no-data.svg


Some files were not shown because too many files changed in this diff