Browse Source

提交项目

zhontai 2 years ago
commit
a71f9d5f46
100 changed files with 19447 additions and 0 deletions
  1. 23 0
      .editorconfig
  2. 8 0
      .env
  3. 5 0
      .env.development
  4. 5 0
      .env.production
  5. 19 0
      .eslintignore
  6. 76 0
      .eslintrc.js
  7. 17 0
      .github/workflows/gitee-sync.yml
  8. 23 0
      .gitignore
  9. 39 0
      .prettierrc.js
  10. 21 0
      LICENSE
  11. 120 0
      README.md
  12. 12 0
      bin/build.bat
  13. 12 0
      bin/install.bat
  14. 12 0
      bin/run-web.bat
  15. 37 0
      gen/gen-admin-api.js
  16. 12 0
      gen/gen-templates.js
  17. 28 0
      gen/templates/api.ejs
  18. 37 0
      gen/templates/data-contract-jsdoc.ejs
  19. 28 0
      gen/templates/data-contracts.ejs
  20. 12 0
      gen/templates/enum-data-contract.ejs
  21. 409 0
      gen/templates/http-client.ejs
  22. 10 0
      gen/templates/interface-data-contract.ejs
  23. 28 0
      gen/templates/object-field-jsdoc.ejs
  24. 100 0
      gen/templates/procedure-call.ejs
  25. 30 0
      gen/templates/route-docs.ejs
  26. 27 0
      gen/templates/route-name.ejs
  27. 22 0
      gen/templates/route-type.ejs
  28. 18 0
      gen/templates/route-types.ejs
  29. 15 0
      gen/templates/type-data-contract.ejs
  30. 26 0
      index.html
  31. 6570 0
      package-lock.json
  32. 84 0
      package.json
  33. BIN
      public/favicon.ico
  34. 93 0
      src/App.vue
  35. 211 0
      src/api/admin/Api.ts
  36. 146 0
      src/api/admin/Auth.ts
  37. 57 0
      src/api/admin/Cache.ts
  38. 168 0
      src/api/admin/Dictionary.ts
  39. 168 0
      src/api/admin/DictionaryType.ts
  40. 386 0
      src/api/admin/Document.ts
  41. 54 0
      src/api/admin/LoginLog.ts
  42. 54 0
      src/api/admin/OprationLog.ts
  43. 147 0
      src/api/admin/Org.ts
  44. 457 0
      src/api/admin/Permission.ts
  45. 297 0
      src/api/admin/Role.ts
  46. 192 0
      src/api/admin/Task.ts
  47. 35 0
      src/api/admin/TaskLog.ts
  48. 168 0
      src/api/admin/Tenant.ts
  49. 369 0
      src/api/admin/User.ts
  50. 190 0
      src/api/admin/View.ts
  51. 3707 0
      src/api/admin/data-contracts.ts
  52. 408 0
      src/api/admin/http-client.ts
  53. 27 0
      src/api/login/index.ts
  54. 30 0
      src/api/menu/index.ts
  55. 6 0
      src/assets/login-bg.svg
  56. 0 0
      src/assets/login-icon-two.svg
  57. 0 0
      src/assets/login-main.svg
  58. 33 0
      src/assets/logo-mini.svg
  59. 26 0
      src/components/auth/auth.vue
  60. 27 0
      src/components/auth/authAll.vue
  61. 32 0
      src/components/auth/auths.vue
  62. 143 0
      src/components/cropper/index.vue
  63. 101 0
      src/components/editor/index.vue
  64. 241 0
      src/components/iconSelector/index.vue
  65. 84 0
      src/components/iconSelector/list.vue
  66. 96 0
      src/components/my-date-range/index.vue
  67. 48 0
      src/components/my-dropdown-more/index.vue
  68. 251 0
      src/components/my-select-icon/icon-select.vue
  69. 64 0
      src/components/my-select-icon/index.vue
  70. 191 0
      src/components/noticeBar/index.vue
  71. 63 0
      src/components/svgIcon/index.vue
  72. 256 0
      src/components/table/index.vue
  73. 40 0
      src/directive/authDirective.ts
  74. 178 0
      src/directive/customDirective.ts
  75. 18 0
      src/directive/index.ts
  76. 9 0
      src/globalProperties/index.ts
  77. 95 0
      src/globalProperties/modal.ts
  78. 68 0
      src/i18n/index.ts
  79. 192 0
      src/i18n/lang/en.ts
  80. 192 0
      src/i18n/lang/zh-cn.ts
  81. 192 0
      src/i18n/lang/zh-tw.ts
  82. 13 0
      src/i18n/pages/formI18n/en.ts
  83. 13 0
      src/i18n/pages/formI18n/zh-cn.ts
  84. 13 0
      src/i18n/pages/formI18n/zh-tw.ts
  85. 29 0
      src/i18n/pages/login/en.ts
  86. 28 0
      src/i18n/pages/login/zh-cn.ts
  87. 28 0
      src/i18n/pages/login/zh-tw.ts
  88. 154 0
      src/layout/component/aside.vue
  89. 272 0
      src/layout/component/columnsAside.vue
  90. 18 0
      src/layout/component/header.vue
  91. 65 0
      src/layout/component/main.vue
  92. 25 0
      src/layout/footer/index.vue
  93. 50 0
      src/layout/index.vue
  94. 352 0
      src/layout/lockScreen/index.vue
  95. 75 0
      src/layout/logo/index.vue
  96. 71 0
      src/layout/main/classic.vue
  97. 71 0
      src/layout/main/columns.vue
  98. 71 0
      src/layout/main/defaults.vue
  99. 58 0
      src/layout/main/transverse.vue
  100. 146 0
      src/layout/navBars/breadcrumb/breadcrumb.vue

+ 23 - 0
.editorconfig

@@ -0,0 +1,23 @@
+# https://editorconfig.org
+root = true
+
+# 匹配全部文件
+[*]
+# 设置字符集
+charset = utf-8
+# 缩进风格,可选space、tab
+indent_style = space
+# 缩进的空格数
+indent_size = 2
+# 结尾换行符,可选lf、cr、crlf
+end_of_line = lf
+# 在文件结尾插入新行
+insert_final_newline = true
+# 删除一行中的前后空格
+trim_trailing_whitespace = true
+
+# 匹配md结尾的文件
+[*.md]
+indent_style = tab
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 8 - 0
.env

@@ -0,0 +1,8 @@
+# port 端口号
+VITE_PORT = 8100
+
+# open 运行 npm run dev 时自动打开浏览器
+VITE_OPEN = true
+
+# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
+VITE_PUBLIC_PATH = ''

+ 5 - 0
.env.development

@@ -0,0 +1,5 @@
+# 本地环境
+ENV = 'development'
+
+# 本地环境接口地址
+VITE_API_URL = 'http://localhost:8100/'

+ 5 - 0
.env.production

@@ -0,0 +1,5 @@
+# 线上环境
+ENV = 'production'
+
+# 线上环境接口地址
+VITE_API_URL = 'http://localhost:8100/'

+ 19 - 0
.eslintignore

@@ -0,0 +1,19 @@
+
+*.sh
+node_modules
+lib
+*.md
+*.scss
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+mock
+public
+bin
+build
+config
+index.html
+src/assets
+gen

+ 76 - 0
.eslintrc.js

@@ -0,0 +1,76 @@
+module.exports = {
+  root: true,
+  env: {
+    browser: true,
+    es2021: true,
+    node: true,
+  },
+  parser: 'vue-eslint-parser',
+  parserOptions: {
+    ecmaVersion: 12,
+    parser: '@typescript-eslint/parser',
+    sourceType: 'module',
+  },
+  extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
+  plugins: ['vue', '@typescript-eslint'],
+  overrides: [
+    {
+      files: ['*.ts', '*.tsx', '*.vue'],
+      rules: {
+        'no-undef': 'off',
+      },
+    },
+  ],
+  rules: {
+    // http://eslint.cn/docs/rules/
+    // https://eslint.vuejs.org/rules/
+    // https://typescript-eslint.io/rules/no-unused-vars/
+    '@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',
+    '@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-redeclare': 'error',
+    '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
+    '@typescript-eslint/no-unused-vars': [2],
+    'vue/custom-event-name-casing': '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/no-multiple-template-root': 'off',
+    'vue/require-default-prop': 'off',
+    'vue/no-v-model-argument': 'off',
+    'vue/no-arrow-functions-in-watch': 'off',
+    'vue/no-template-key': 'off',
+    'vue/no-v-html': 'off',
+    'vue/comment-directive': 'off',
+    'vue/no-parsing-error': 'off',
+    'vue/no-deprecated-v-on-native-modifier': 'off',
+    'vue/multi-word-component-names': 'off',
+    'no-useless-escape': 'off',
+    'no-sparse-arrays': 'off',
+    'no-prototype-builtins': 'off',
+    'no-constant-condition': 'off',
+    'no-use-before-define': 'off',
+    'no-restricted-globals': 'off',
+    'no-restricted-syntax': 'off',
+    'generator-star-spacing': 'off',
+    'no-unreachable': 'off',
+    'no-multiple-template-root': 'off',
+    'no-unused-vars': 'error',
+    'no-v-model-argument': 'off',
+    'no-case-declarations': 'off',
+    'no-console': 'error',
+    'no-redeclare': 'off',
+  },
+}

+ 17 - 0
.github/workflows/gitee-sync.yml

@@ -0,0 +1,17 @@
+name: Publish
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Sync to Gitee 💕
+        uses: wearerequired/git-mirror-action@master
+        env:
+          SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY_HOME }}
+        with:
+          source-repo: 'git@github.com:zhontai/admin.ui.plus.git'
+          destination-repo: 'git@gitee.com:zhontai/admin.ui.plus.git'

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/dist
+
+
+# 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?

+ 39 - 0
.prettierrc.js

@@ -0,0 +1,39 @@
+module.exports = {
+  // 一行最多多少个字符
+  printWidth: 150,
+  // 指定每个缩进级别的空格数
+  tabWidth: 2,
+  // 使用制表符而不是空格缩进行
+  useTabs: false,
+  // 在语句末尾打印分号
+  semi: false,
+  // 使用单引号而不是双引号
+  singleQuote: true,
+  // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
+  quoteProps: 'as-needed',
+  // 在JSX中使用单引号而不是双引号
+  jsxSingleQuote: false,
+  // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
+  trailingComma: 'es5',
+  // 在对象文字中的括号之间打印空格
+  bracketSpacing: true,
+  // jsx 标签的反尖括号需要换行
+  jsxBracketSameLine: false,
+  // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
+  arrowParens: 'always',
+  // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
+  rangeStart: 0,
+  rangeEnd: Infinity,
+  // 指定要使用的解析器,不需要写文件开头的 @prettier
+  requirePragma: false,
+  // 不需要自动在文件开头插入 @prettier
+  insertPragma: false,
+  // 使用默认的折行标准 always\never\preserve
+  proseWrap: 'preserve',
+  // 指定HTML文件的全局空格敏感度 css\strict\ignore
+  htmlWhitespaceSensitivity: 'css',
+  // Vue文件脚本和样式标签缩进
+  vueIndentScriptAndStyle: false,
+  // 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
+  endOfLine: 'lf',
+}

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 zhontai
+
+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.

+ 120 - 0
README.md

@@ -0,0 +1,120 @@
+<div align="center">
+	<h2>中台admin.ui.plus</h2>
+	<p align="center">
+	    <a href="https://v3.vuejs.org/" target="_blank">
+	        <img src="https://img.shields.io/badge/vue.js-vue3.x-green" alt="vue">
+	    </a>
+	    <a href="https://element-plus.gitee.io/#/zh-CN/component/changelog" target="_blank">
+	        <img src="https://img.shields.io/badge/element--plus-%3E1.0.0-blue" alt="element plus">
+	    </a>
+		<a href="https://www.tslang.cn/" target="_blank">
+	        <img src="https://img.shields.io/badge/typescript-%3E4.0.0-blue" alt="typescript">
+	    </a>
+		<a href="https://vitejs.dev/" target="_blank">
+		    <img src="https://img.shields.io/badge/vite-%3E2.0.0-yellow" alt="vite">
+		</a>
+		<a href="https://github.com/zhontai/admin.ui.plus/blob/master/LICENSE" target="_blank">
+		    <img src="https://img.shields.io/badge/license-MIT-success" alt="license">
+		</a>
+	</p>
+	<p>&nbsp;</p>
+</div>
+
+#### 🌈 介绍
+
+基于 vue3.x + CompositionAPI setup 语法糖 + typescript + vite + element plus + vue-router-next + pinia 技术,适配手机、平板、pc 的后台权限管理框架,希望减少工作量,帮助大家实现快速开发。
+
+#### ⛱️ 线上预览
+
+- vue3.x 版本预览(admin.ui.plus)<a href="https://admin.zhontai.net/login" target="_blank">https://admin.zhontai.net/login</a>
+
+#### 💒 代码仓库
+
+- vue3.x 版本 <a href="https://github.com/zhontai/admin.ui.plus" target="_blank">https://github.com/zhontai/admin.ui.plus</a>
+
+#### 🚧 安装 cnpm、yarn
+
+- 复制代码(桌面 cmd 运行) `npm install -g cnpm --registry=https://registry.npm.taobao.org`
+- 复制代码(桌面 cmd 运行) `npm install -g yarn`
+
+#### 🏭 环境支持
+
+| Edge      | Firefox      | Chrome      | Safari      |
+| --------- | ------------ | ----------- | ----------- |
+| Edge ≥ 88 | Firefox ≥ 78 | Chrome ≥ 87 | Safari ≥ 13 |
+
+> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。
+
+#### ⚡ 使用说明
+
+建议使用 cnpm,因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 14.18+/16+</a>
+
+> Vite 不再支持 Node 12 / 13 / 15,因为上述版本已经进入了 EOL 阶段。现在你必须使用 Node 14.18+ / 16+ 版本。
+
+```bash
+# 克隆项目
+git clone https://github.com/zhontai/admin.ui.plus/git
+
+# 进入项目
+cd admin.ui.plus
+
+# 安装依赖
+cnpm install
+
+# 运行项目
+cnpm run dev
+
+# 打包发布
+cnpm run build
+```
+
+#### 📚 开发文档
+
+- 查看开发文档:<a href="https://www.zhontai.net" target="_blank">admin.ui.plus.doc</a>
+
+#### 💯 学习交流加 QQ 群
+
+> 中台 admin 开发群(2000 人群)。
+
+- 中台 admin 开发群:<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=99e2f5cbf895d14aa61f4d038f3cfcb4a778f69e04e529394ada1bb307e6ded4">1058693879</a>
+
+  <a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=99e2f5cbf895d14aa61f4d038f3cfcb4a778f69e04e529394ada1bb307e6ded4">
+  	<img src="https://www.zhontai.net/imgs/qq-group-1058693879.png" width="220" height="220" alt="中台admin 开发群" title="中台admin 开发群"/>
+  </a>
+
+#### ❤️ 鸣谢列表
+
+- <a href="https://github.com/vuejs/vue" target="_blank">vue</a>
+- <a href="https://github.com/vuejs/vue-next" target="_blank">vue-next</a>
+- <a href="https://github.com/ElemeFE/element" target="_blank">element-ui</a>
+- <a href="https://github.com/element-plus/element-plus" target="_blank">element-plus</a>
+- <a href="https://github.com/vuejs/vue-router-next" target="_blank">vue-router-next</a>
+- <a href="https://github.com/vuejs/pinia" target="_blank">pinia</a>
+- <a href="https://github.com/apache/echarts" target="_blank">echarts</a>
+- <a href="https://github.com/axios/axios" target="_blank">axios</a>
+- <a href="https://github.com/zenorocha/clipboard.js" target="_blank">clipboard</a>
+- <a href="https://github.com/inorganik/countUp.js" target="_blank">countUp</a>
+- <a href="https://github.com/developit/mitt" target="_blank">mitt</a>
+- <a href="https://github.com/rstacruz/nprogress" target="_blank">nprogress</a>
+- <a href="https://github.com/sindresorhus/screenfull.js" target="_blank">screenfull</a>
+- <a href="https://github.com/SortableJS/Sortable" target="_blank">sortablejs</a>
+- <a href="https://github.com/sass/sass" target="_blank">sass</a>
+- <a href="https://github.com/microsoft/TypeScript" target="_blank">typescript</a>
+- <a href="https://github.com/vitejs/vite" target="_blank">vite</a>
+- <a href="https://github.com/wangeditor-team/wangEditor" target="_blank">wangeditor</a>
+- <a href="https://github.com/fengyuanchen/cropperjs" target="_blank">cropperjs</a>
+- <a href="https://github.com/davidshimjs/qrcodejs" target="_blank">qrcodejs</a>
+- <a href="https://github.com/crabbly/Print.js" target="_blank">print-js</a>
+- <a href="https://github.com/jbaysolutions/vue-grid-layout" target="_blank">vue-grid-layout</a>
+- <a href="https://github.com/antoniandre/splitpanes" target="_blank">splitpanes</a>
+- <a href="https://github.com/jsplumb/jsplumb" target="_blank">jsplumb</a>
+- <a href="https://github.com/hxj9102/table2excel" target="_blank">js-table2excel</a>
+
+#### 💕 特别感谢
+
+- <a href="https://github.com/lyt-Top/vue-next-admin" target="_blank">vue-next-admin</a>
+
+#### 💌 支持作者
+
+如果觉得框架不错,或者已经在使用了,希望你可以去 <a target="_blank" href="https://github.com/zhontai/admin.ui.plus">Github</a> 或者
+<a target="_blank" href="https://gitee.com/zhontai/admin.ui.plus">Gitee</a> 帮我点个 ⭐ Star,这将是对我极大的鼓励与支持。

+ 12 - 0
bin/build.bat

@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo 发布网站,生成dist文件
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm run build
+
+pause

+ 12 - 0
bin/install.bat

@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo 安装包,生成node_modules文件
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm install --registry=https://registry.npmmirror.com
+
+pause

+ 12 - 0
bin/run-web.bat

@@ -0,0 +1,12 @@
+@echo off
+echo.
+echo 运行网站
+echo.
+
+%~d0
+cd %~dp0
+
+cd ..
+npm run dev
+
+pause

+ 37 - 0
gen/gen-admin-api.js

@@ -0,0 +1,37 @@
+const { generateApi } = require('swagger-typescript-api')
+const path = require('path')
+const fs = require('fs')
+
+const apis = [
+  {
+    output: path.resolve(__dirname, '../src/api/admin'),
+    url: 'http://localhost:8000/swagger/admin/swagger.json',
+  },
+]
+
+apis?.forEach((api) => {
+  generateApi({
+    output: api.output,
+    templates: path.resolve(__dirname, './templates'),
+    url: api.url,
+    httpClientType: 'axios',
+    modular: true,
+    cleanOutput: true,
+    moduleNameIndex: 2, // 0 api, 1 api htt-client data-contracts, 2 apis htt-client data-contracts
+    moduleNameFirstTag: true, //apis htt-client data-contracts
+    unwrapResponseData: true,
+    generateUnionEnums: true,
+    defaultResponseType: 'AxiosResponse',
+    // hooks: {
+    //   onFormatTypeName: (typeName, rawTypeName, schemaType) => {
+
+    //   },
+    // }
+  })
+    .then((r) => {
+      // files.forEach(({ content, name }) => {
+      //   fs.writeFile(path, content);
+      // });
+    })
+    .catch((e) => console.error(e))
+})

+ 12 - 0
gen/gen-templates.js

@@ -0,0 +1,12 @@
+const { generateTemplates } = require('swagger-typescript-api')
+const path = require('path')
+
+//导出默认模板
+generateTemplates({
+  cleanOutput: false,
+  output: path.resolve(__dirname, './templates'),
+  httpClientType: 'axios',
+  modular: true,
+  silent: false,
+  rewrite: false,
+})

+ 28 - 0
gen/templates/api.ejs

@@ -0,0 +1,28 @@
+<%
+const { utils, route, config, modelTypes } = it;
+const { _, pascalCase, require } = utils;
+const apiClassName = pascalCase(route.moduleName);
+const routes = route.routes;
+const dataContracts = _.map(modelTypes, "name");
+%>
+
+<% if (config.httpClientType === config.constants.HTTP_CLIENT.AXIOS) { %> import { AxiosRequestConfig, AxiosResponse } from "axios"; <% } %>
+
+import { HttpClient, RequestParams, ContentType, HttpResponse } from "./<%~ config.fileNames.httpClient %>";
+<% if (dataContracts.length) { %>
+import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
+<% } %>
+
+export class <%= apiClassName %><SecurityDataType = unknown><% if (!config.singleHttpClient) { %> extends HttpClient<SecurityDataType> <% } %> {
+<% if(config.singleHttpClient) { %>
+  http: HttpClient<SecurityDataType>;
+
+  constructor (http: HttpClient<SecurityDataType>) {
+    this.http = http;
+  }
+<% } %>
+
+    <% routes.forEach((route) => { %>
+        <%~ includeFile('./procedure-call.ejs', { ...it, route }) %>
+    <% }) %>
+}

+ 37 - 0
gen/templates/data-contract-jsdoc.ejs

@@ -0,0 +1,37 @@
+<%
+const { data, utils } = it;
+const { formatDescription, require, _ } = utils;
+
+const stringify = (value) => _.isObject(value) ? JSON.stringify(value) : _.isString(value) ? `"${value}"` : value
+
+const jsDocLines = _.compact([
+    data.title,
+    data.description && formatDescription(data.description),
+    !_.isUndefined(data.deprecated) && data.deprecated && '@deprecated',
+    !_.isUndefined(data.format) && `@format ${data.format}`,
+    !_.isUndefined(data.minimum) && `@min ${data.minimum}`,
+    !_.isUndefined(data.multipleOf) && `@multipleOf ${data.multipleOf}`,
+    !_.isUndefined(data.exclusiveMinimum) && `@exclusiveMin ${data.exclusiveMinimum}`,
+    !_.isUndefined(data.maximum) && `@max ${data.maximum}`,
+    !_.isUndefined(data.minLength) && `@minLength ${data.minLength}`,
+    !_.isUndefined(data.maxLength) && `@maxLength ${data.maxLength}`,
+    !_.isUndefined(data.exclusiveMaximum) && `@exclusiveMax ${data.exclusiveMaximum}`,
+    !_.isUndefined(data.maxItems) && `@maxItems ${data.maxItems}`,
+    !_.isUndefined(data.minItems) && `@minItems ${data.minItems}`,
+    !_.isUndefined(data.uniqueItems) && `@uniqueItems ${data.uniqueItems}`,
+    !_.isUndefined(data.default) && `@default ${stringify(data.default)}`,
+    !_.isUndefined(data.pattern) && `@pattern ${data.pattern}`,
+    !_.isUndefined(data.example) && `@example ${stringify(data.example)}`
+]).join('\n').split('\n');
+%>
+<% if (jsDocLines.every(_.isEmpty)) { %>
+<% } else if (jsDocLines.length === 1) { %>
+/** <%~ jsDocLines[0] %> */
+<% } else if (jsDocLines.length) { %>
+/**
+<% for (jsDocLine of jsDocLines) { %>
+ * <%~ jsDocLine %>
+
+<% } %>
+ */
+<% } %>

+ 28 - 0
gen/templates/data-contracts.ejs

@@ -0,0 +1,28 @@
+<%
+const { modelTypes, utils, config } = it;
+const { formatDescription, require, _, Ts } = utils;
+
+
+const dataContractTemplates = {
+  enum: (contract) => {
+    return `enum ${contract.name} {\r\n${contract.content} \r\n }`;
+  },
+  interface: (contract) => {
+    return `interface ${contract.name} {\r\n${contract.content}}`;
+  },
+  type: (contract) => {
+    return `type ${contract.name} = ${contract.content}`;
+  },
+}
+%>
+
+<% if (config.internalTemplateOptions.addUtilRequiredKeysType) { %>
+type <%~ config.Ts.CodeGenKeyword.UtilRequiredKeys %><T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
+<% } %>
+
+<% modelTypes.forEach((contract) => { %>
+  <%~ includeFile('./data-contract-jsdoc.ejs', { ...it, data: { ...contract, ...contract.typeData } }) %>
+  export <%~ (dataContractTemplates[contract.typeIdentifier] || dataContractTemplates.type)(contract) %>
+
+
+<% }) %>

+ 12 - 0
gen/templates/enum-data-contract.ejs

@@ -0,0 +1,12 @@
+<%
+const { contract, utils, config } = it;
+const { formatDescription, require, _ } = utils;
+const { name, $content } = contract;
+%>
+<% if (config.generateUnionEnums) { %>
+  export type <%~ name %> = <%~ _.map($content, ({ value }) => value).join(" | ") %>
+<% } else { %>
+  export enum <%~ name %> {
+    <%~ _.map($content, ({ key, value }) => `${key} = ${value}`).join(",\n") %>
+  }
+<% } %>

+ 409 - 0
gen/templates/http-client.ejs

@@ -0,0 +1,409 @@
+<%
+const { apiConfig, generateResponses, config } = it;
+%>
+
+import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from 'axios'
+import { ElLoading, ElMessage, LoadingOptions } from 'element-plus'
+import { Local, Session } from '/@/utils/storage'
+
+export const adminTokenKey = 'admin-token'
+
+// 获得token
+export const getToken = () => {
+  return Local.get(adminTokenKey)
+}
+// 设置token
+export const setToken = (token: any) => {
+  return Local.set(adminTokenKey, token)
+}
+// 清除token
+export const clearToken = () => {
+  Local.remove(adminTokenKey)
+  Session.remove('token')
+  window.requests = []
+  window.location.reload()
+}
+
+export type QueryParamsType = Record<string | number, any>;
+
+export interface FullRequestParams extends Omit<AxiosRequestConfig, "data" | "params" | "url" | "responseType"> {
+  /** set parameter to `true` for call `securityWorker` for this request */
+  secure?: boolean;
+  /** request path */
+  path: string;
+  /** content type of request body */
+  type?: ContentType;
+  /** query params */
+  query?: QueryParamsType;
+  /** format of response (i.e. response.json() -> format: "json") */
+  format?: ResponseType;
+  /** request body */
+  body?: unknown;
+  /** 显示错误消息 */
+  showErrorMessage?: boolean
+  /** 显示成功消息 */
+  showSuccessMessage?: boolean
+  /** 登录访问 */
+  login?: boolean
+  /** 加载中 */
+  loading?: boolean
+  loadingOptions?: LoadingOptions
+  /** 取消重复请求 */
+  cancelRepeatRequest?: boolean
+}
+
+export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
+
+export interface ApiConfig<SecurityDataType = unknown> extends Omit<AxiosRequestConfig, "data" | "cancelToken"> {
+  securityWorker?: (securityData: SecurityDataType | null) => Promise<AxiosRequestConfig | void> | AxiosRequestConfig | void;
+  secure?: boolean;
+  format?: ResponseType;
+}
+
+export enum ContentType {
+  Json = "application/json",
+  FormData = "multipart/form-data",
+  UrlEncoded = "application/x-www-form-urlencoded",
+  Text = "text/plain",
+}
+
+export interface LoadingInstance {
+  target: any
+  count: number
+}
+
+const pendingMap = new Map()
+
+const loadingInstance: LoadingInstance = {
+  target: null,
+  count: 0,
+}
+
+export class HttpClient<SecurityDataType = unknown> {
+    public instance: AxiosInstance;
+    private securityData: SecurityDataType | null = null;
+    private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
+    private secure?: boolean;
+    private format?: ResponseType;
+
+    constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig<SecurityDataType> = {}) {
+        this.instance = axios.create({ ...axiosConfig, timeout: 60000, baseURL: axiosConfig.baseURL || "<%~ apiConfig.baseUrl %>" })
+        this.secure = secure;
+        this.format = format;
+        this.securityWorker = securityWorker;
+    }
+
+    public setSecurityData = (data: SecurityDataType | null) => {
+        this.securityData = data
+    }
+
+    protected mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig {
+      const method = params1.method || (params2 && params2.method)
+
+      return {
+        ...this.instance.defaults,
+        ...params1,
+        ...(params2 || {}),
+        headers: {
+          ...((method && this.instance.defaults.headers[method.toLowerCase() as keyof HeadersDefaults]) || {}),
+          ...(params1.headers || {}),
+          ...((params2 && params2.headers) || {}),
+        },
+      };
+    }
+
+    protected stringifyFormItem(formItem: unknown) {
+      if (typeof formItem === "object" && formItem !== null) {
+        return JSON.stringify(formItem);
+      } else {
+        return `${formItem}`;
+      }
+    }
+
+    protected createFormData(input: Record<string, unknown>): FormData {
+      return Object.keys(input || {}).reduce((formData, key) => {
+        const property = input[key];
+        const propertyContent: any[] = (property instanceof Array) ? property : [property]
+
+        for (const formItem of propertyContent) {
+          const isFileType = formItem instanceof Blob || formItem instanceof File;
+          formData.append(
+            key,
+            isFileType ? formItem : this.stringifyFormItem(formItem)
+            );
+        }
+
+        return formData;
+      }, new FormData());
+    }
+
+    /**
+    * 错误处理
+    * @param {*} error
+    */
+   protected errorHandle(error: any) {
+     if (!error) {
+       return
+     }
+     if (axios.isCancel(error)) return console.error('请求重复已被自动取消:' + error.message)
+     let message = ''
+     if (error.response) {
+       switch (error.response.status) {
+         case 302:
+           message = '接口重定向'
+           break
+         case 400:
+           message = '参数不正确'
+           break
+         case 401:
+           message = '您还没有登录'
+           break
+         case 403:
+           message = '您没有权限操作'
+           break
+         case 404:
+           message = '请求地址出错:' + error.response.config.url
+           break
+         case 408:
+           message = '请求超时'
+           break
+         case 409:
+           message = '系统已存在相同数据'
+           break
+         case 500:
+           message = '服务器内部错误'
+           break
+         case 501:
+           message = '服务未实现'
+           break
+         case 502:
+           message = '网关错误'
+           break
+         case 503:
+           message = '服务不可用'
+           break
+         case 504:
+           message = '服务暂时无法访问,请稍后再试'
+           break
+         case 505:
+           message = 'HTTP版本不受支持'
+           break
+         default:
+           message = '异常问题,请联系网站管理员'
+           break
+       }
+     }
+     if (error.message.includes('timeout')) message = '请求超时'
+     if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常' : '您已断网'
+ 
+     if (message) {
+       ElMessage.error({ message })
+     }
+   }
+ 
+    /**
+    * 刷新token
+    * @param {*} config
+    */
+    protected async refreshToken(config: any) {
+      const token = getToken()
+      if (!token) {
+        clearToken()
+        return Promise.reject(config)
+      }
+
+      if (window.tokenRefreshing) {
+        return new Promise((resolve) => {
+          window.requests.push(() => {
+            resolve(this.instance(config))
+          })
+        })
+      }
+
+      window.tokenRefreshing = true
+
+      return this.request<AxiosResponse, any>({
+        path: `/api/admin/auth/refresh`,
+        method: 'GET',
+        secure: true,
+        format: 'json',
+        login: false,
+        query: {
+          token: token,
+        },
+      })
+      .then((res) => {
+        if (res?.success) {
+          const token = res.data.token
+          setToken(token)
+          window.requests.forEach((apiRequest) => apiRequest())
+          window.requests = []
+          return this.instance(config)
+        } else {
+          clearToken()
+          return Promise.reject(res)
+        }
+      })
+      .catch((error) => {
+        clearToken()
+        return Promise.reject(error)
+      })
+      .finally(() => {
+        window.tokenRefreshing = false
+      })
+    }
+
+    /**
+    * 储存每个请求的唯一cancel回调, 以此为标识
+    */
+    protected addPending(config: AxiosRequestConfig) {
+      const pendingKey = this.getPendingKey(config)
+      config.cancelToken =
+        config.cancelToken ||
+        new axios.CancelToken((cancel) => {
+          if (!pendingMap.has(pendingKey)) {
+            pendingMap.set(pendingKey, cancel)
+          }
+        })
+    }
+
+    /**
+    * 删除重复的请求
+    */
+    protected removePending(config: AxiosRequestConfig) {
+      const pendingKey = this.getPendingKey(config)
+      if (pendingMap.has(pendingKey)) {
+        const cancelToken = pendingMap.get(pendingKey)
+        cancelToken(pendingKey)
+        pendingMap.delete(pendingKey)
+      }
+    }
+
+    /**
+    * 生成每个请求的唯一key
+    */
+    protected getPendingKey(config: AxiosRequestConfig) {
+      let { data } = config
+      const { url, method, params, headers } = config
+      if (typeof data === 'string') data = JSON.parse(data)
+      return [url, method, headers && headers.Authorization ? headers.Authorization : '', JSON.stringify(params), JSON.stringify(data)].join('&')
+    }
+
+    /**
+    * 关闭Loading层实例
+    */
+    protected closeLoading(loading: boolean = false) {
+      if (loading && loadingInstance.count > 0) loadingInstance.count--
+      if (loadingInstance.count === 0) {
+        loadingInstance.target.close()
+        loadingInstance.target = null
+      }
+    }
+
+    public request = async <T = any, _E = any>({
+        secure,
+        path,
+        type,
+        query,
+        format,
+        body,
+        showErrorMessage = true,
+        showSuccessMessage = false,
+        login = true,
+        loading = false,
+        loadingOptions = {},
+        cancelRepeatRequest = false,
+        ...params
+<% if (config.unwrapResponseData) { %>
+    }: FullRequestParams): Promise<T> => {
+<% } else { %>
+    }: FullRequestParams): Promise<AxiosResponse<T>> => {
+<% } %>
+        const secureParams = ((typeof secure === 'boolean' ? secure : this.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {};
+        const requestParams = this.mergeRequestParams(params, secureParams);
+        const responseFormat = (format || this.format) || undefined;
+
+        if (type === ContentType.FormData && body && body !== null && typeof body === "object") {
+          body = this.createFormData(body as Record<string, unknown>);
+        }
+
+        if (type === ContentType.Text && body && body !== null && typeof body !== "string") {
+          body = JSON.stringify(body);
+        }
+
+        // 请求拦截
+        this.instance.interceptors.request.use(
+          (config) => {
+            this.removePending(config)
+            cancelRepeatRequest && this.addPending(config)
+
+            if (loading) {
+              loadingInstance.count++
+              if (loadingInstance.count === 1) {
+                loadingInstance.target = ElLoading.service(loadingOptions)
+              }
+            }
+
+            const accessToken = getToken()
+            config.headers!['Authorization'] = `Bearer ${accessToken}`
+            return config
+          },
+          (error) => {
+            return Promise.reject(error)
+          }
+        )
+        // 响应拦截
+        this.instance.interceptors.response.use(
+          (res) => {
+            this.removePending(res.config)
+            loading && this.closeLoading(loading)
+
+            const data = res.data
+            if (data.success) {
+              if (showSuccessMessage) {
+                ElMessage.success({ message: data.msg ? data.msg : '操作成功' })
+              }
+            } else {
+              if (showErrorMessage) {
+                ElMessage.error({ message: data.msg ? data.msg : '操作失败' })
+              }
+              // return Promise.reject(res)
+            }
+
+            return res
+          },
+          async (error) => {
+            error.config && this.removePending(error.config)
+            loading && this.closeLoading(loading)
+
+            //刷新token
+            if (login && error?.response?.status === 401) {
+              return this.refreshToken(error.config)
+            }
+
+            //错误处理
+            if (showErrorMessage) {
+              this.errorHandle(error)
+            }
+
+            return Promise.reject(error)
+          }
+        )
+
+        return this.instance.request({
+            ...requestParams,
+            headers: {
+                ...(requestParams.headers || {}),
+                ...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
+            },
+            params: query,
+            responseType: responseFormat,
+            data: body,
+            url: path,
+<% if (config.unwrapResponseData) { %>
+        }).then(response => response.data);
+<% } else { %>
+        });
+<% } %>
+    };
+}

+ 10 - 0
gen/templates/interface-data-contract.ejs

@@ -0,0 +1,10 @@
+<%
+const { contract, utils } = it;
+const { formatDescription, require, _ } = utils;
+%>
+export interface <%~ contract.name %> {
+  <% _.forEach(contract.$content, (field) => { %>
+    <%~ includeFile('./object-field-jsdoc.ejs', { ...it, field }) %>
+    <%~ field.name %><%~ field.isRequired ? '' : '?' %>: <%~ field.value %><%~ field.isNullable ? ' | null' : ''%>;
+  <% }) %>
+}

+ 28 - 0
gen/templates/object-field-jsdoc.ejs

@@ -0,0 +1,28 @@
+<%
+const { field, utils } = it;
+const { formatDescription, require, _ } = utils;
+
+const comments = _.uniq(
+    _.compact([
+        field.title,
+        field.description,
+        field.deprecated && ` * @deprecated`,
+        !_.isUndefined(field.format) && `@format ${field.format}`,
+        !_.isUndefined(field.minimum) && `@min ${field.minimum}`,
+        !_.isUndefined(field.maximum) && `@max ${field.maximum}`,
+        !_.isUndefined(field.pattern) && `@pattern ${field.pattern}`,
+        !_.isUndefined(field.example) &&
+        `@example ${_.isObject(field.example) ? JSON.stringify(field.example) : field.example}`,
+    ]).reduce((acc, comment) => [...acc, ...comment.split(/\n/g)], []),
+);
+%>
+<% if (comments.length === 1) { %>
+  /** <%~ comments[0] %> */
+<% } else if (comments.length) { %>
+  /**
+  <% comments.forEach(comment => { %>
+   * <%~ comment %>
+
+  <% }) %>
+   */
+<% } %>

+ 100 - 0
gen/templates/procedure-call.ejs

@@ -0,0 +1,100 @@
+<%
+const { utils, route, config } = it;
+const { requestBodyInfo, responseBodyInfo, specificArgNameResolver } = route;
+const { _, getInlineParseContent, getParseContent, parseSchema, getComponentByRef, require } = utils;
+const { parameters, path, method, payload, query, formData, security, requestParams } = route.request;
+const { type, errorType, contentTypes } = route.response;
+const { HTTP_CLIENT, RESERVED_REQ_PARAMS_ARG_NAMES } = config.constants;
+const routeDocs = includeFile("./route-docs", { config, route, utils });
+const queryName = (query && query.name) || "query";
+const pathParams = _.values(parameters);
+const pathParamsNames = _.map(pathParams, "name");
+
+const isFetchTemplate = config.httpClientType === HTTP_CLIENT.FETCH;
+
+const requestConfigParam = {
+    name: specificArgNameResolver.resolve(RESERVED_REQ_PARAMS_ARG_NAMES),
+    optional: true,
+    type: "RequestParams",
+    defaultValue: "{}",
+}
+
+const argToTmpl = ({ name, optional, type, defaultValue }) => `${name}${!defaultValue && optional ? '?' : ''}: ${type}${defaultValue ? ` = ${defaultValue}` : ''}`;
+
+const rawWrapperArgs = config.extractRequestParams ?
+    _.compact([
+        requestParams && {
+          name: pathParams.length ? `{ ${_.join(pathParamsNames, ", ")}, ...${queryName} }` : queryName,
+          optional: false,
+          type: getInlineParseContent(requestParams),
+        },
+        ...(!requestParams ? pathParams : []),
+        payload,
+        requestConfigParam,
+    ]) :
+    _.compact([
+        ...pathParams,
+        query,
+        payload,
+        requestConfigParam,
+    ])
+
+const wrapperArgs = _
+    // Sort by optionality
+    .sortBy(rawWrapperArgs, [o => o.optional])
+    .map(argToTmpl)
+    .join(', ')
+
+// RequestParams["type"]
+const requestContentKind = {
+    "JSON": "ContentType.Json",
+    "URL_ENCODED": "ContentType.UrlEncoded",
+    "FORM_DATA": "ContentType.FormData",
+    "TEXT": "ContentType.Text",
+}
+// RequestParams["format"]
+const responseContentKind = {
+    "JSON": '"json"',
+    "IMAGE": '"blob"',
+    "FORM_DATA": isFetchTemplate ? '"formData"' : '"document"'
+}
+
+const bodyTmpl = _.get(payload, "name") || null;
+const queryTmpl = (query != null && queryName) || null;
+const bodyContentKindTmpl = requestContentKind[requestBodyInfo.contentKind] || null;
+const responseFormatTmpl = responseContentKind[responseBodyInfo.success && responseBodyInfo.success.schema && responseBodyInfo.success.schema.contentKind] || null;
+const securityTmpl = security ? 'true' : null;
+
+const describeReturnType = () => {
+    if (!config.toJS) return "";
+
+    switch(config.httpClientType) {
+        case HTTP_CLIENT.AXIOS: {
+          return `Promise<AxiosResponse<${type}>>`
+        }
+        default: {
+          return `Promise<HttpResponse<${type}, ${errorType}>`
+        }
+    }
+}
+
+%>
+/**
+<%~ routeDocs.description %>
+
+ *<% /* Here you can add some other JSDoc tags */ %>
+
+<%~ routeDocs.lines %>
+
+ */
+<%~ route.routeName.usage %> = (<%~ wrapperArgs %>)<%~ config.toJS ? `: ${describeReturnType()}` : "" %> =>
+    <%~ config.singleHttpClient ? 'this.http.request' : 'this.request' %><<%~ type %>, <%~ errorType %>>({
+        path: `<%~ path %>`,
+        method: '<%~ _.upperCase(method) %>',
+        <%~ queryTmpl ? `query: ${queryTmpl},` : '' %>
+        <%~ bodyTmpl ? `body: ${bodyTmpl},` : '' %>
+        <%~ securityTmpl ? `secure: ${securityTmpl},` : '' %>
+        <%~ bodyContentKindTmpl ? `type: ${bodyContentKindTmpl},` : '' %>
+        <%~ responseFormatTmpl ? `format: ${responseFormatTmpl},` : '' %>
+        ...<%~ _.get(requestConfigParam, "name") %>,
+    })

+ 30 - 0
gen/templates/route-docs.ejs

@@ -0,0 +1,30 @@
+<%
+const { config, route, utils } = it;
+const { _, formatDescription, fmtToJSDocLine, pascalCase, require } = utils;
+const { raw, request, routeName } = route;
+
+const jsDocDescription = raw.description ?
+    ` * @description ${formatDescription(raw.description, true)}` :
+    fmtToJSDocLine('No description', { eol: false });
+const jsDocLines = _.compact([
+    _.size(raw.tags) && ` * @tags ${raw.tags.join(", ")}`,
+    ` * @name ${pascalCase(routeName.usage)}`,
+    raw.summary && ` * @summary ${raw.summary}`,
+    ` * @request ${_.upperCase(request.method)}:${raw.route}`,
+    raw.deprecated && ` * @deprecated`,
+    routeName.duplicate && ` * @originalName ${routeName.original}`,
+    routeName.duplicate && ` * @duplicate`,
+    request.security && ` * @secure`,
+    ...(config.generateResponses && raw.responsesTypes.length
+    ? raw.responsesTypes.map(
+        ({ type, status, description, isSuccess }) =>
+            ` * @response \`${status}\` \`${_.replace(_.replace(type, /\/\*/g, "\\*"), /\*\//g, "*\\")}\` ${description}`,
+        )
+    : []),
+]).map(str => str.trimEnd()).join("\n");
+
+return {
+  description: jsDocDescription,
+  lines: jsDocLines,
+}
+%>

+ 27 - 0
gen/templates/route-name.ejs

@@ -0,0 +1,27 @@
+<%
+const { routeInfo, utils } = it;
+const {
+  operationId,
+  method,
+  route,
+  moduleName,
+  responsesTypes,
+  description,
+  tags,
+  summary,
+  pathArgs,
+} = routeInfo;
+const { _, fmtToJSDocLine, require } = utils;
+
+const createCustomOperationId = (method, route, moduleName) => {
+  const hasPathInserts = /\{(\w){1,}\}/g.test(route);
+  const splitedRouteBySlash = _.compact(_.replace(route, /\{(\w){1,}\}/g, "").split("/"));
+  const routeParts = (splitedRouteBySlash.length > 1
+    ? [splitedRouteBySlash[splitedRouteBySlash.length-1]]
+    : splitedRouteBySlash
+  ).join("_");
+  return _.camelCase(_.lowerCase(routeParts));
+};
+
+return createCustomOperationId(method, route, moduleName);
+%>

+ 22 - 0
gen/templates/route-type.ejs

@@ -0,0 +1,22 @@
+<%
+const { route, utils, config } = it;
+const { _, pascalCase, require } = utils;
+const { query, payload, pathParams, headers } = route.request;
+
+const routeDocs = includeFile("./route-docs", { config, route, utils });
+const routeNamespace = pascalCase(route.routeName.usage);
+
+%>
+/**
+<%~ routeDocs.description %>
+
+<%~ routeDocs.lines %>
+
+*/
+export namespace <%~ routeNamespace %> {
+  export type RequestParams = <%~ (pathParams && pathParams.type) || '{}' %>;
+  export type RequestQuery = <%~ (query && query.type) || '{}' %>;
+  export type RequestBody = <%~ (payload && payload.type) || 'never' %>;
+  export type RequestHeaders = <%~ (headers && headers.type) || '{}' %>;
+  export type ResponseBody = <%~ route.response.type %>;
+}

+ 18 - 0
gen/templates/route-types.ejs

@@ -0,0 +1,18 @@
+<%
+const { utils, config, route, modelTypes } = it;
+const { _, pascalCase } = utils;
+const { routes, moduleName } = route;
+const dataContracts = config.modular ? _.map(modelTypes, "name") : [];
+
+%>
+<% if (dataContracts.length) { %>
+import { <%~ dataContracts.join(", ") %> } from "./<%~ config.fileNames.dataContracts %>"
+<% } %>
+
+export namespace <%~ pascalCase(moduleName) %> {
+    <% _.forEach(routes, (route) => { %>
+
+        <%~ includeFile('./route-type.ejs', { ...it, route }) %>
+
+    <% }) %>
+}

+ 15 - 0
gen/templates/type-data-contract.ejs

@@ -0,0 +1,15 @@
+<%
+const { contract, utils } = it;
+const { formatDescription, require, _ } = utils;
+
+%>
+<% if (contract.$content.length) { %>
+export type <%~ contract.name %> = {
+  <% _.forEach(contract.$content, (field) => { %>
+    <%~ includeFile('./object-field-jsdoc.ejs', { ...it, field }) %>
+    <%~ field.field %>;
+  <% }) %>
+}<%~ utils.isNeedToAddNull(contract) ? ' | null' : ''%>
+<% } else { %>
+export type <%~ contract.name %> = Record<string, any>;
+<% } %>

+ 26 - 0
index.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta name="keywords" content="中台Admin" />
+    <meta name="description" content="中台Admin" />
+    <link rel="icon" href="/favicon.ico" />
+    <title>中台Admin</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- <script type="text/javascript">
+      var _hmt = _hmt || []
+      ;(function () {
+        var hm = document.createElement('script')
+        hm.src = ''
+        var s = document.getElementsByTagName('script')[0]
+        s.parentNode.insertBefore(hm, s)
+      })()
+    </script> -->
+    <script type="module" src="/src/main.ts"></script>
+    <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script>
+  </body>
+</html>

+ 6570 - 0
package-lock.json

@@ -0,0 +1,6570 @@
+{
+  "name": "admin.ui.plus",
+  "version": "1.0.0",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "admin.ui.plus",
+      "version": "1.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "@element-plus/icons-vue": "^2.0.10",
+        "@wangeditor/editor": "^5.1.23",
+        "@wangeditor/editor-for-vue": "^5.1.12",
+        "axios": "^1.2.1",
+        "countup.js": "^2.3.2",
+        "cropperjs": "^1.5.13",
+        "echarts": "^5.4.1",
+        "echarts-gl": "^2.0.9",
+        "echarts-wordcloud": "^2.1.0",
+        "element-plus": "^2.2.26",
+        "js-cookie": "^3.0.1",
+        "js-table2excel": "^1.0.3",
+        "jsplumb": "^2.15.6",
+        "mitt": "^3.0.0",
+        "nprogress": "^0.2.0",
+        "pinia": "^2.0.28",
+        "print-js": "^1.6.0",
+        "qrcodejs2-fixes": "^0.0.2",
+        "qs": "^6.11.0",
+        "screenfull": "^6.0.2",
+        "sortablejs": "^1.15.0",
+        "splitpanes": "^3.1.5",
+        "vue": "^3.2.45",
+        "vue-clipboard3": "^2.0.0",
+        "vue-grid-layout": "^3.0.0-beta1",
+        "vue-i18n": "^9.2.2",
+        "vue-router": "^4.1.6"
+      },
+      "devDependencies": {
+        "@types/node": "^18.11.13",
+        "@types/nprogress": "^0.2.0",
+        "@types/sortablejs": "^1.15.0",
+        "@typescript-eslint/eslint-plugin": "^5.46.0",
+        "@typescript-eslint/parser": "^5.46.0",
+        "@vitejs/plugin-vue": "^4.0.0",
+        "@vue/compiler-sfc": "^3.2.45",
+        "dotenv": "16.0.3",
+        "eslint": "^8.29.0",
+        "eslint-plugin-vue": "^9.8.0",
+        "prettier": "^2.8.1",
+        "sass": "^1.56.2",
+        "swagger-typescript-api": "12.0.2",
+        "typescript": "^4.9.4",
+        "vite": "^4.0.0",
+        "vite-plugin-compression": "0.5.1",
+        "vite-plugin-vue-setup-extend": "^0.4.0",
+        "vue-eslint-parser": "^9.1.0"
+      },
+      "engines": {
+        "node": ">=16.0.0",
+        "npm": ">= 7.0.0"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.18.6",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/highlight": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.19.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight": {
+      "version": "7.18.6",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/chalk": {
+      "version": "2.4.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-convert": {
+      "version": "1.9.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-name": {
+      "version": "1.1.3",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/has-flag": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/supports-color": {
+      "version": "5.5.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.20.5",
+      "license": "MIT",
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.20.6",
+      "license": "MIT",
+      "dependencies": {
+        "regenerator-runtime": "^0.13.11"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.5.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@element-plus/icons-vue": {
+      "version": "2.0.10",
+      "license": "MIT",
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.16.6",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@eslint/eslintrc": {
+      "version": "1.3.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.4.0",
+        "globals": "^13.15.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/@exodus/schemasafe": {
+      "version": "1.0.0-rc.9",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@floating-ui/core": {
+      "version": "1.0.4",
+      "license": "MIT"
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.0.10",
+      "license": "MIT",
+      "dependencies": {
+        "@floating-ui/core": "^1.0.4"
+      }
+    },
+    "node_modules/@humanwhocodes/config-array": {
+      "version": "0.11.7",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@humanwhocodes/object-schema": "^1.2.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      },
+      "engines": {
+        "node": ">=10.10.0"
+      }
+    },
+    "node_modules/@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.22"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/nzakas"
+      }
+    },
+    "node_modules/@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@interactjs/actions": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/auto-scroll": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/auto-start": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/core": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "peerDependencies": {
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/dev-tools": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/modifiers": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/inertia": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "dependencies": {
+        "@interactjs/offset": "1.10.17"
+      },
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/modifiers": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/interact": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "dependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/types": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/interactjs": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "dependencies": {
+        "@interactjs/actions": "1.10.17",
+        "@interactjs/auto-scroll": "1.10.17",
+        "@interactjs/auto-start": "1.10.17",
+        "@interactjs/core": "1.10.17",
+        "@interactjs/dev-tools": "1.10.17",
+        "@interactjs/inertia": "1.10.17",
+        "@interactjs/interact": "1.10.17",
+        "@interactjs/modifiers": "1.10.17",
+        "@interactjs/offset": "1.10.17",
+        "@interactjs/pointer-events": "1.10.17",
+        "@interactjs/reflow": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/modifiers": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "dependencies": {
+        "@interactjs/snappers": "1.10.17"
+      },
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/offset": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/pointer-events": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/reflow": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/snappers": {
+      "version": "1.10.17",
+      "license": "MIT",
+      "optionalDependencies": {
+        "@interactjs/interact": "1.10.17"
+      },
+      "peerDependencies": {
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "node_modules/@interactjs/types": {
+      "version": "1.10.17",
+      "license": "MIT"
+    },
+    "node_modules/@interactjs/utils": {
+      "version": "1.10.17",
+      "license": "MIT"
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "9.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/devtools-if": "9.2.2",
+        "@intlify/message-compiler": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/devtools-if": {
+      "version": "9.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "9.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/shared": "9.2.2",
+        "source-map": "0.6.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "9.2.2",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/vue-devtools": {
+      "version": "9.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "name": "@sxzz/popperjs-es",
+      "version": "2.11.7",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/popperjs"
+      }
+    },
+    "node_modules/@transloadit/prettier-bytes": {
+      "version": "0.0.7",
+      "license": "MIT"
+    },
+    "node_modules/@types/event-emitter": {
+      "version": "0.3.3",
+      "license": "MIT"
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.11",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.14.191",
+      "license": "MIT"
+    },
+    "node_modules/@types/lodash-es": {
+      "version": "4.17.6",
+      "license": "MIT",
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "18.11.15",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/nprogress": {
+      "version": "0.2.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/semver": {
+      "version": "7.3.13",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/sortablejs": {
+      "version": "1.15.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/swagger-schema-official": {
+      "version": "2.0.22",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.16",
+      "license": "MIT"
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "5.46.1",
+        "@typescript-eslint/type-utils": "5.46.1",
+        "@typescript-eslint/utils": "5.46.1",
+        "debug": "^4.3.4",
+        "ignore": "^5.2.0",
+        "natural-compare-lite": "^1.4.0",
+        "regexpp": "^3.2.0",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^5.0.0",
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "5.46.1",
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/typescript-estree": "5.46.1",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/visitor-keys": "5.46.1"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/type-utils": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/typescript-estree": "5.46.1",
+        "@typescript-eslint/utils": "5.46.1",
+        "debug": "^4.3.4",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/visitor-keys": "5.46.1",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "@types/semver": "^7.3.12",
+        "@typescript-eslint/scope-manager": "5.46.1",
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/typescript-estree": "5.46.1",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^3.0.0",
+        "semver": "^7.3.7"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "5.46.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "5.46.1",
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@uppy/companion-client": {
+      "version": "2.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "@uppy/utils": "^4.1.2",
+        "namespace-emitter": "^2.0.1"
+      }
+    },
+    "node_modules/@uppy/core": {
+      "version": "2.3.4",
+      "license": "MIT",
+      "dependencies": {
+        "@transloadit/prettier-bytes": "0.0.7",
+        "@uppy/store-default": "^2.1.1",
+        "@uppy/utils": "^4.1.3",
+        "lodash.throttle": "^4.1.1",
+        "mime-match": "^1.0.2",
+        "namespace-emitter": "^2.0.1",
+        "nanoid": "^3.1.25",
+        "preact": "^10.5.13"
+      }
+    },
+    "node_modules/@uppy/store-default": {
+      "version": "2.1.1",
+      "license": "MIT"
+    },
+    "node_modules/@uppy/utils": {
+      "version": "4.1.3",
+      "license": "MIT",
+      "dependencies": {
+        "lodash.throttle": "^4.1.1"
+      }
+    },
+    "node_modules/@uppy/xhr-upload": {
+      "version": "2.1.3",
+      "license": "MIT",
+      "dependencies": {
+        "@uppy/companion-client": "^2.2.2",
+        "@uppy/utils": "^4.1.2",
+        "nanoid": "^3.1.25"
+      },
+      "peerDependencies": {
+        "@uppy/core": "^2.3.3"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/shared": "3.2.45",
+        "estree-walker": "^2.0.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.45",
+        "@vue/compiler-dom": "3.2.45",
+        "@vue/compiler-ssr": "3.2.45",
+        "@vue/reactivity-transform": "3.2.45",
+        "@vue/shared": "3.2.45",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7",
+        "postcss": "^8.1.10",
+        "source-map": "^0.6.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.4.5",
+      "license": "MIT"
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "node_modules/@vue/reactivity-transform": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.45",
+        "@vue/shared": "3.2.45",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/runtime-core": "3.2.45",
+        "@vue/shared": "3.2.45",
+        "csstype": "^2.6.8"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.2.45",
+        "@vue/shared": "3.2.45"
+      },
+      "peerDependencies": {
+        "vue": "3.2.45"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.2.45",
+      "license": "MIT"
+    },
+    "node_modules/@vueuse/core": {
+      "version": "9.6.0",
+      "license": "MIT",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.16",
+        "@vueuse/metadata": "9.6.0",
+        "@vueuse/shared": "9.6.0",
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/core/node_modules/vue-demi": {
+      "version": "0.13.11",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "9.6.0",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "9.6.0",
+      "license": "MIT",
+      "dependencies": {
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared/node_modules/vue-demi": {
+      "version": "0.13.11",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@wangeditor/basic-modules": {
+      "version": "1.1.7",
+      "license": "MIT",
+      "dependencies": {
+        "is-url": "^1.2.4"
+      },
+      "peerDependencies": {
+        "@wangeditor/core": "1.x",
+        "dom7": "^3.0.0",
+        "lodash.throttle": "^4.1.1",
+        "nanoid": "^3.2.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/code-highlight": {
+      "version": "1.0.3",
+      "license": "MIT",
+      "dependencies": {
+        "prismjs": "^1.23.0"
+      },
+      "peerDependencies": {
+        "@wangeditor/core": "1.x",
+        "dom7": "^3.0.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/core": {
+      "version": "1.1.19",
+      "license": "MIT",
+      "dependencies": {
+        "@types/event-emitter": "^0.3.3",
+        "event-emitter": "^0.3.5",
+        "html-void-elements": "^2.0.0",
+        "i18next": "^20.4.0",
+        "scroll-into-view-if-needed": "^2.2.28",
+        "slate-history": "^0.66.0"
+      },
+      "peerDependencies": {
+        "@uppy/core": "^2.1.1",
+        "@uppy/xhr-upload": "^2.0.3",
+        "dom7": "^3.0.0",
+        "is-hotkey": "^0.2.0",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.clonedeep": "^4.5.0",
+        "lodash.debounce": "^4.0.8",
+        "lodash.foreach": "^4.5.0",
+        "lodash.isequal": "^4.5.0",
+        "lodash.throttle": "^4.1.1",
+        "lodash.toarray": "^4.4.0",
+        "nanoid": "^3.2.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/editor": {
+      "version": "5.1.23",
+      "license": "MIT",
+      "dependencies": {
+        "@uppy/core": "^2.1.1",
+        "@uppy/xhr-upload": "^2.0.3",
+        "@wangeditor/basic-modules": "^1.1.7",
+        "@wangeditor/code-highlight": "^1.0.3",
+        "@wangeditor/core": "^1.1.19",
+        "@wangeditor/list-module": "^1.0.5",
+        "@wangeditor/table-module": "^1.1.4",
+        "@wangeditor/upload-image-module": "^1.0.2",
+        "@wangeditor/video-module": "^1.1.4",
+        "dom7": "^3.0.0",
+        "is-hotkey": "^0.2.0",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.clonedeep": "^4.5.0",
+        "lodash.debounce": "^4.0.8",
+        "lodash.foreach": "^4.5.0",
+        "lodash.isequal": "^4.5.0",
+        "lodash.throttle": "^4.1.1",
+        "lodash.toarray": "^4.4.0",
+        "nanoid": "^3.2.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/editor-for-vue": {
+      "version": "5.1.12",
+      "license": "MIT",
+      "peerDependencies": {
+        "@wangeditor/editor": ">=5.1.0",
+        "vue": "^3.0.5"
+      }
+    },
+    "node_modules/@wangeditor/list-module": {
+      "version": "1.0.5",
+      "license": "MIT",
+      "peerDependencies": {
+        "@wangeditor/core": "1.x",
+        "dom7": "^3.0.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/table-module": {
+      "version": "1.1.4",
+      "license": "MIT",
+      "peerDependencies": {
+        "@wangeditor/core": "1.x",
+        "dom7": "^3.0.0",
+        "lodash.isequal": "^4.5.0",
+        "lodash.throttle": "^4.1.1",
+        "nanoid": "^3.2.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/upload-image-module": {
+      "version": "1.0.2",
+      "license": "MIT",
+      "peerDependencies": {
+        "@uppy/core": "^2.0.3",
+        "@uppy/xhr-upload": "^2.0.3",
+        "@wangeditor/basic-modules": "1.x",
+        "@wangeditor/core": "1.x",
+        "dom7": "^3.0.0",
+        "lodash.foreach": "^4.5.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/@wangeditor/video-module": {
+      "version": "1.1.4",
+      "license": "MIT",
+      "peerDependencies": {
+        "@uppy/core": "^2.1.4",
+        "@uppy/xhr-upload": "^2.0.7",
+        "@wangeditor/core": "1.x",
+        "dom7": "^3.0.0",
+        "nanoid": "^3.2.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.8.1",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/array-union": {
+      "version": "2.1.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/async-validator": {
+      "version": "4.2.5",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.2.1",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/batch-processor": {
+      "version": "1.0.0",
+      "license": "MIT"
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/call-me-maybe": {
+      "version": "1.0.2",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/chokidar/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/claygl": {
+      "version": "1.3.0"
+    },
+    "node_modules/clipboard": {
+      "version": "2.0.11",
+      "license": "MIT",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/compute-scroll-into-view": {
+      "version": "1.0.20",
+      "license": "MIT"
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cosmiconfig": {
+      "version": "7.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/countup.js": {
+      "version": "2.3.2",
+      "license": "MIT"
+    },
+    "node_modules/cropperjs": {
+      "version": "1.5.13",
+      "license": "MIT"
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "2.6.21",
+      "license": "MIT"
+    },
+    "node_modules/d": {
+      "version": "1.0.1",
+      "license": "ISC",
+      "dependencies": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "node_modules/data-uri-to-buffer": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.7",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.3.4",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "2.1.2"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "license": "MIT"
+    },
+    "node_modules/didyoumean": {
+      "version": "1.2.2",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/dir-glob": {
+      "version": "3.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/doctrine": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "esutils": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/dom7": {
+      "version": "3.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "ssr-window": "^3.0.0-alpha.1"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "16.0.3",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/echarts": {
+      "version": "5.4.1",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "2.3.0",
+        "zrender": "5.4.1"
+      }
+    },
+    "node_modules/echarts-gl": {
+      "version": "2.0.9",
+      "dependencies": {
+        "claygl": "^1.2.1",
+        "zrender": "^5.1.1"
+      },
+      "peerDependencies": {
+        "echarts": "^5.1.2"
+      }
+    },
+    "node_modules/echarts-wordcloud": {
+      "version": "2.1.0",
+      "license": "ISC",
+      "peerDependencies": {
+        "echarts": "^5.0.1"
+      }
+    },
+    "node_modules/element-plus": {
+      "version": "2.2.26",
+      "license": "MIT",
+      "dependencies": {
+        "@ctrl/tinycolor": "^3.4.1",
+        "@element-plus/icons-vue": "^2.0.6",
+        "@floating-ui/dom": "^1.0.1",
+        "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+        "@types/lodash": "^4.14.182",
+        "@types/lodash-es": "^4.17.6",
+        "@vueuse/core": "^9.1.0",
+        "async-validator": "^4.2.5",
+        "dayjs": "^1.11.3",
+        "escape-html": "^1.0.3",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "lodash-unified": "^1.0.2",
+        "memoize-one": "^6.0.0",
+        "normalize-wheel-es": "^1.2.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/element-resize-detector": {
+      "version": "1.2.4",
+      "license": "MIT",
+      "dependencies": {
+        "batch-processor": "1.0.0"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es5-ext": {
+      "version": "0.10.62",
+      "hasInstallScript": true,
+      "license": "ISC",
+      "dependencies": {
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "next-tick": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/es6-iterator": {
+      "version": "2.0.3",
+      "license": "MIT",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/es6-promise": {
+      "version": "3.3.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es6-symbol": {
+      "version": "3.1.3",
+      "license": "ISC",
+      "dependencies": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.16.6",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.16.6",
+        "@esbuild/android-arm64": "0.16.6",
+        "@esbuild/android-x64": "0.16.6",
+        "@esbuild/darwin-arm64": "0.16.6",
+        "@esbuild/darwin-x64": "0.16.6",
+        "@esbuild/freebsd-arm64": "0.16.6",
+        "@esbuild/freebsd-x64": "0.16.6",
+        "@esbuild/linux-arm": "0.16.6",
+        "@esbuild/linux-arm64": "0.16.6",
+        "@esbuild/linux-ia32": "0.16.6",
+        "@esbuild/linux-loong64": "0.16.6",
+        "@esbuild/linux-mips64el": "0.16.6",
+        "@esbuild/linux-ppc64": "0.16.6",
+        "@esbuild/linux-riscv64": "0.16.6",
+        "@esbuild/linux-s390x": "0.16.6",
+        "@esbuild/linux-x64": "0.16.6",
+        "@esbuild/netbsd-x64": "0.16.6",
+        "@esbuild/openbsd-x64": "0.16.6",
+        "@esbuild/sunos-x64": "0.16.6",
+        "@esbuild/win32-arm64": "0.16.6",
+        "@esbuild/win32-ia32": "0.16.6",
+        "@esbuild/win32-x64": "0.16.6"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "license": "MIT"
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "8.29.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint/eslintrc": "^1.3.3",
+        "@humanwhocodes/config-array": "^0.11.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.1.1",
+        "eslint-utils": "^3.0.0",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.4.0",
+        "esquery": "^1.4.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.15.0",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-sdsl": "^4.1.4",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "regexpp": "^3.2.0",
+        "strip-ansi": "^6.0.1",
+        "strip-json-comments": "^3.1.0",
+        "text-table": "^0.2.0"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/eslint-plugin-vue": {
+      "version": "9.8.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-utils": "^3.0.0",
+        "natural-compare": "^1.4.0",
+        "nth-check": "^2.0.1",
+        "postcss-selector-parser": "^6.0.9",
+        "semver": "^7.3.5",
+        "vue-eslint-parser": "^9.0.1",
+        "xml-name-validator": "^4.0.0"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/eslint-utils": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^2.0.0"
+      },
+      "engines": {
+        "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": ">=5"
+      }
+    },
+    "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "2.1.0",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "3.3.0",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/eslint-scope": {
+      "version": "7.1.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/eslint/node_modules/estraverse": {
+      "version": "5.3.0",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/espree": {
+      "version": "9.4.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.8.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.4.0",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esquery/node_modules/estraverse": {
+      "version": "5.3.0",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esrecurse/node_modules/estraverse": {
+      "version": "5.3.0",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "4.3.0",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "license": "MIT"
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/eta": {
+      "version": "1.12.3",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/eta-dev/eta?sponsor=1"
+      }
+    },
+    "node_modules/event-emitter": {
+      "version": "0.3.5",
+      "license": "MIT",
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
+    "node_modules/ext": {
+      "version": "1.7.0",
+      "license": "ISC",
+      "dependencies": {
+        "type": "^2.7.2"
+      }
+    },
+    "node_modules/ext/node_modules/type": {
+      "version": "2.7.2",
+      "license": "ISC"
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.2.12",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-safe-stringify": {
+      "version": "2.1.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fastq": {
+      "version": "1.14.0",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fetch-blob": {
+      "version": "3.2.0",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "node-domexception": "^1.0.0",
+        "web-streams-polyfill": "^3.0.3"
+      },
+      "engines": {
+        "node": "^12.20 || >= 14.13"
+      }
+    },
+    "node_modules/file-entry-cache": {
+      "version": "6.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^3.0.4"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "3.0.4",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      },
+      "engines": {
+        "node": "^10.12.0 || >=12.0.0"
+      }
+    },
+    "node_modules/flatted": {
+      "version": "3.2.7",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.2",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/formdata-polyfill": {
+      "version": "4.0.10",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fetch-blob": "^3.1.2"
+      },
+      "engines": {
+        "node": ">=12.20.0"
+      }
+    },
+    "node_modules/fs-extra": {
+      "version": "10.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "license": "MIT"
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.1.3",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/globals": {
+      "version": "13.19.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/globby": {
+      "version": "11.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.10",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/grapheme-splitter": {
+      "version": "1.0.4",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.3",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/html-void-elements": {
+      "version": "2.0.1",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/http2-client": {
+      "version": "1.3.5",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/i18next": {
+      "version": "20.6.1",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.12.0"
+      }
+    },
+    "node_modules/ignore": {
+      "version": "5.2.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/immer": {
+      "version": "9.0.16",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/immer"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "4.1.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.11.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-hotkey": {
+      "version": "0.2.0",
+      "license": "MIT"
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-path-inside": {
+      "version": "3.0.3",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-plain-object": {
+      "version": "5.0.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-url": {
+      "version": "1.2.4",
+      "license": "MIT"
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/js-cookie": {
+      "version": "3.0.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/js-sdsl": {
+      "version": "4.2.0",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/js-sdsl"
+      }
+    },
+    "node_modules/js-table2excel": {
+      "version": "1.0.3",
+      "license": "ISC"
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/jsonfile": {
+      "version": "6.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/jsplumb": {
+      "version": "2.15.6",
+      "license": "(MIT OR GPL-2.0)"
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "license": "MIT"
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "license": "MIT"
+    },
+    "node_modules/lodash-unified": {
+      "version": "1.0.3",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/lodash-es": "*",
+        "lodash": "*",
+        "lodash-es": "*"
+      }
+    },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "license": "MIT"
+    },
+    "node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "license": "MIT"
+    },
+    "node_modules/lodash.debounce": {
+      "version": "4.0.8",
+      "license": "MIT"
+    },
+    "node_modules/lodash.foreach": {
+      "version": "4.5.0",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isequal": {
+      "version": "4.5.0",
+      "license": "MIT"
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.throttle": {
+      "version": "4.1.1",
+      "license": "MIT"
+    },
+    "node_modules/lodash.toarray": {
+      "version": "4.4.0",
+      "license": "MIT"
+    },
+    "node_modules/lru-cache": {
+      "version": "6.0.0",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.25.9",
+      "license": "MIT",
+      "dependencies": {
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "3.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-dir/node_modules/semver": {
+      "version": "6.3.0",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "license": "MIT"
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-match": {
+      "version": "1.0.2",
+      "license": "ISC",
+      "dependencies": {
+        "wildcard": "^1.1.0"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mitt": {
+      "version": "3.0.0",
+      "license": "MIT"
+    },
+    "node_modules/ms": {
+      "version": "2.1.2",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/namespace-emitter": {
+      "version": "2.0.1",
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.4",
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/natural-compare-lite": {
+      "version": "1.4.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/next-tick": {
+      "version": "1.1.0",
+      "license": "ISC"
+    },
+    "node_modules/node-domexception": {
+      "version": "1.0.0",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "github",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.5.0"
+      }
+    },
+    "node_modules/node-emoji": {
+      "version": "1.11.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash": "^4.17.21"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "3.3.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/node-fetch"
+      }
+    },
+    "node_modules/node-fetch-h2": {
+      "version": "2.3.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "http2-client": "^1.2.5"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      }
+    },
+    "node_modules/node-readfiles": {
+      "version": "0.2.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "es6-promise": "^3.2.1"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-wheel-es": {
+      "version": "1.2.0",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/nprogress": {
+      "version": "0.2.0",
+      "license": "MIT"
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/oas-kit-common": {
+      "version": "1.0.8",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "fast-safe-stringify": "^2.0.7"
+      }
+    },
+    "node_modules/oas-linter": {
+      "version": "3.2.2",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@exodus/schemasafe": "^1.0.0-rc.2",
+        "should": "^13.2.1",
+        "yaml": "^1.10.0"
+      },
+      "funding": {
+        "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+      }
+    },
+    "node_modules/oas-resolver": {
+      "version": "2.5.6",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "node-fetch-h2": "^2.3.0",
+        "oas-kit-common": "^1.0.8",
+        "reftools": "^1.1.9",
+        "yaml": "^1.10.0",
+        "yargs": "^17.0.1"
+      },
+      "bin": {
+        "resolve": "resolve.js"
+      },
+      "funding": {
+        "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+      }
+    },
+    "node_modules/oas-schema-walker": {
+      "version": "1.1.5",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "funding": {
+        "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+      }
+    },
+    "node_modules/oas-validator": {
+      "version": "5.0.8",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "call-me-maybe": "^1.0.1",
+        "oas-kit-common": "^1.0.8",
+        "oas-linter": "^3.2.2",
+        "oas-resolver": "^2.5.6",
+        "oas-schema-walker": "^1.1.5",
+        "reftools": "^1.1.9",
+        "should": "^13.2.1",
+        "yaml": "^1.10.0"
+      },
+      "funding": {
+        "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.12.2",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "2.0.28",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.4.5",
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.4.0",
+        "typescript": ">=4.4.4",
+        "vue": "^2.6.14 || ^3.2.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia/node_modules/vue-demi": {
+      "version": "0.13.11",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.20",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.4",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "6.0.11",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/preact": {
+      "version": "10.11.3",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/preact"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "2.8.1",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/print-js": {
+      "version": "1.6.0",
+      "license": "MIT"
+    },
+    "node_modules/prismjs": {
+      "version": "1.29.0",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "license": "MIT"
+    },
+    "node_modules/punycode": {
+      "version": "2.1.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qrcodejs2-fixes": {
+      "version": "0.0.2",
+      "license": "MIT"
+    },
+    "node_modules/qs": {
+      "version": "6.11.0",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.0.4"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/reftools": {
+      "version": "1.1.9",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "funding": {
+        "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "license": "MIT"
+    },
+    "node_modules/regexpp": {
+      "version": "3.2.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-core-module": "^2.9.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.0.4",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "3.7.4",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=14.18.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/sass": {
+      "version": "1.56.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/screenfull": {
+      "version": "6.0.2",
+      "license": "MIT",
+      "engines": {
+        "node": "^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/scroll-into-view-if-needed": {
+      "version": "2.2.31",
+      "license": "MIT",
+      "dependencies": {
+        "compute-scroll-into-view": "^1.0.20"
+      }
+    },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "license": "MIT"
+    },
+    "node_modules/semver": {
+      "version": "7.3.8",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "lru-cache": "^6.0.0"
+      },
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/should": {
+      "version": "13.2.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "should-equal": "^2.0.0",
+        "should-format": "^3.0.3",
+        "should-type": "^1.4.0",
+        "should-type-adaptors": "^1.0.1",
+        "should-util": "^1.0.0"
+      }
+    },
+    "node_modules/should-equal": {
+      "version": "2.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "should-type": "^1.4.0"
+      }
+    },
+    "node_modules/should-format": {
+      "version": "3.0.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "should-type": "^1.3.0",
+        "should-type-adaptors": "^1.0.1"
+      }
+    },
+    "node_modules/should-type": {
+      "version": "1.4.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/should-type-adaptors": {
+      "version": "1.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "should-type": "^1.3.0",
+        "should-util": "^1.0.0"
+      }
+    },
+    "node_modules/should-util": {
+      "version": "1.0.1",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/side-channel": {
+      "version": "1.0.4",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/slash": {
+      "version": "3.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/slate": {
+      "version": "0.72.8",
+      "license": "MIT",
+      "dependencies": {
+        "immer": "^9.0.6",
+        "is-plain-object": "^5.0.0",
+        "tiny-warning": "^1.0.3"
+      }
+    },
+    "node_modules/slate-history": {
+      "version": "0.66.0",
+      "license": "MIT",
+      "dependencies": {
+        "is-plain-object": "^5.0.0"
+      },
+      "peerDependencies": {
+        "slate": ">=0.65.3"
+      }
+    },
+    "node_modules/snabbdom": {
+      "version": "3.5.1",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.3.0"
+      }
+    },
+    "node_modules/sortablejs": {
+      "version": "1.15.0",
+      "license": "MIT"
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sourcemap-codec": {
+      "version": "1.4.8",
+      "license": "MIT"
+    },
+    "node_modules/splitpanes": {
+      "version": "3.1.5",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antoniandre"
+      }
+    },
+    "node_modules/ssr-window": {
+      "version": "3.0.0",
+      "license": "MIT"
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/swagger-schema-official": {
+      "version": "2.0.0-bab6bed",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/swagger-typescript-api": {
+      "version": "12.0.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/swagger-schema-official": "2.0.22",
+        "cosmiconfig": "7.0.1",
+        "didyoumean": "^1.2.2",
+        "eta": "1.12.3",
+        "js-yaml": "4.1.0",
+        "lodash": "4.17.21",
+        "make-dir": "3.1.0",
+        "nanoid": "3.3.4",
+        "node-emoji": "1.11.0",
+        "node-fetch": "^3.2.10",
+        "prettier": "2.7.1",
+        "swagger-schema-official": "2.0.0-bab6bed",
+        "swagger2openapi": "7.0.8",
+        "typescript": "4.8.4"
+      },
+      "bin": {
+        "sta": "index.js",
+        "swagger-typescript-api": "index.js"
+      }
+    },
+    "node_modules/swagger-typescript-api/node_modules/prettier": {
+      "version": "2.7.1",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/swagger-typescript-api/node_modules/typescript": {
+      "version": "4.8.4",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=4.2.0"
+      }
+    },
+    "node_modules/swagger2openapi": {
+      "version": "7.0.8",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "call-me-maybe": "^1.0.1",
+        "node-fetch": "^2.6.1",
+        "node-fetch-h2": "^2.3.0",
+        "node-readfiles": "^0.2.0",
+        "oas-kit-common": "^1.0.8",
+        "oas-resolver": "^2.5.6",
+        "oas-schema-walker": "^1.1.5",
+        "oas-validator": "^5.0.8",
+        "reftools": "^1.1.9",
+        "yaml": "^1.10.0",
+        "yargs": "^17.0.1"
+      },
+      "bin": {
+        "boast": "boast.js",
+        "oas-validate": "oas-validate.js",
+        "swagger2openapi": "swagger2openapi.js"
+      },
+      "funding": {
+        "url": "https://github.com/Mermade/oas-kit?sponsor=1"
+      }
+    },
+    "node_modules/swagger2openapi/node_modules/node-fetch": {
+      "version": "2.6.7",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/text-table": {
+      "version": "0.2.0",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "license": "MIT"
+    },
+    "node_modules/tiny-warning": {
+      "version": "1.0.3",
+      "license": "MIT"
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tslib": {
+      "version": "2.3.0",
+      "license": "0BSD"
+    },
+    "node_modules/tsutils": {
+      "version": "3.21.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^1.8.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      },
+      "peerDependencies": {
+        "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+      }
+    },
+    "node_modules/tsutils/node_modules/tslib": {
+      "version": "1.14.1",
+      "dev": true,
+      "license": "0BSD"
+    },
+    "node_modules/type": {
+      "version": "1.2.0",
+      "license": "ISC"
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "4.9.4",
+      "devOptional": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=4.2.0"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "2.0.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vite": {
+      "version": "4.0.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.16.3",
+        "postcss": "^8.4.20",
+        "resolve": "^1.22.1",
+        "rollup": "^3.7.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "@types/node": ">= 14",
+        "less": "*",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-plugin-compression": {
+      "version": "0.5.1",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.2",
+        "debug": "^4.3.3",
+        "fs-extra": "^10.0.0"
+      },
+      "peerDependencies": {
+        "vite": ">=2.0.0"
+      }
+    },
+    "node_modules/vite-plugin-vue-setup-extend": {
+      "version": "0.4.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-sfc": "^3.2.29",
+        "magic-string": "^0.25.7"
+      },
+      "peerDependencies": {
+        "vite": ">=2.0.0"
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.2.45",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.2.45",
+        "@vue/compiler-sfc": "3.2.45",
+        "@vue/runtime-dom": "3.2.45",
+        "@vue/server-renderer": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "node_modules/vue-clipboard3": {
+      "version": "2.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "clipboard": "^2.0.6"
+      }
+    },
+    "node_modules/vue-eslint-parser": {
+      "version": "9.1.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.3.4",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.3.1",
+        "esquery": "^1.4.0",
+        "lodash": "^4.17.21",
+        "semver": "^7.3.6"
+      },
+      "engines": {
+        "node": "^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mysticatea"
+      },
+      "peerDependencies": {
+        "eslint": ">=6.0.0"
+      }
+    },
+    "node_modules/vue-eslint-parser/node_modules/eslint-scope": {
+      "version": "7.1.1",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/vue-eslint-parser/node_modules/estraverse": {
+      "version": "5.3.0",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/vue-grid-layout": {
+      "version": "3.0.0-beta1",
+      "dependencies": {
+        "@interactjs/actions": "^1.10.2",
+        "@interactjs/auto-start": "^1.10.2",
+        "@interactjs/dev-tools": "^1.10.2",
+        "@interactjs/interactjs": "^1.10.2",
+        "@interactjs/modifiers": "^1.10.2",
+        "element-resize-detector": "^1.2.1",
+        "mitt": "^2.1.0"
+      }
+    },
+    "node_modules/vue-grid-layout/node_modules/mitt": {
+      "version": "2.1.0",
+      "license": "MIT"
+    },
+    "node_modules/vue-i18n": {
+      "version": "9.2.2",
+      "license": "MIT",
+      "dependencies": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/vue-router": {
+      "version": "4.1.6",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.4.5"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/web-streams-polyfill": {
+      "version": "3.2.1",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/wildcard": {
+      "version": "1.1.2",
+      "license": "MIT"
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.3",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/xml-name-validator": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/yaml": {
+      "version": "1.10.2",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.6.2",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zrender": {
+      "version": "5.4.1",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tslib": "2.3.0"
+      }
+    }
+  },
+  "dependencies": {
+    "@babel/code-frame": {
+      "version": "7.18.6",
+      "dev": true,
+      "requires": {
+        "@babel/highlight": "^7.18.6"
+      }
+    },
+    "@babel/helper-validator-identifier": {
+      "version": "7.19.1",
+      "dev": true
+    },
+    "@babel/highlight": {
+      "version": "7.18.6",
+      "dev": true,
+      "requires": {
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "3.2.1",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "color-convert": {
+          "version": "1.9.3",
+          "dev": true,
+          "requires": {
+            "color-name": "1.1.3"
+          }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "dev": true
+        },
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "dev": true
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "@babel/parser": {
+      "version": "7.20.5"
+    },
+    "@babel/runtime": {
+      "version": "7.20.6",
+      "requires": {
+        "regenerator-runtime": "^0.13.11"
+      }
+    },
+    "@ctrl/tinycolor": {
+      "version": "3.5.0"
+    },
+    "@element-plus/icons-vue": {
+      "version": "2.0.10",
+      "requires": {}
+    },
+    "@esbuild/win32-x64": {
+      "version": "0.16.6",
+      "dev": true,
+      "optional": true
+    },
+    "@eslint/eslintrc": {
+      "version": "1.3.3",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.4.0",
+        "globals": "^13.15.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      }
+    },
+    "@exodus/schemasafe": {
+      "version": "1.0.0-rc.9",
+      "dev": true
+    },
+    "@floating-ui/core": {
+      "version": "1.0.4"
+    },
+    "@floating-ui/dom": {
+      "version": "1.0.10",
+      "requires": {
+        "@floating-ui/core": "^1.0.4"
+      }
+    },
+    "@humanwhocodes/config-array": {
+      "version": "0.11.7",
+      "dev": true,
+      "requires": {
+        "@humanwhocodes/object-schema": "^1.2.1",
+        "debug": "^4.1.1",
+        "minimatch": "^3.0.5"
+      }
+    },
+    "@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "dev": true
+    },
+    "@humanwhocodes/object-schema": {
+      "version": "1.2.1",
+      "dev": true
+    },
+    "@interactjs/actions": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/auto-scroll": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/auto-start": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/core": {
+      "version": "1.10.17",
+      "requires": {}
+    },
+    "@interactjs/dev-tools": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/inertia": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17",
+        "@interactjs/offset": "1.10.17"
+      }
+    },
+    "@interactjs/interact": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/core": "1.10.17",
+        "@interactjs/types": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "@interactjs/interactjs": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/actions": "1.10.17",
+        "@interactjs/auto-scroll": "1.10.17",
+        "@interactjs/auto-start": "1.10.17",
+        "@interactjs/core": "1.10.17",
+        "@interactjs/dev-tools": "1.10.17",
+        "@interactjs/inertia": "1.10.17",
+        "@interactjs/interact": "1.10.17",
+        "@interactjs/modifiers": "1.10.17",
+        "@interactjs/offset": "1.10.17",
+        "@interactjs/pointer-events": "1.10.17",
+        "@interactjs/reflow": "1.10.17",
+        "@interactjs/utils": "1.10.17"
+      }
+    },
+    "@interactjs/modifiers": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17",
+        "@interactjs/snappers": "1.10.17"
+      }
+    },
+    "@interactjs/offset": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/pointer-events": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/reflow": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/snappers": {
+      "version": "1.10.17",
+      "requires": {
+        "@interactjs/interact": "1.10.17"
+      }
+    },
+    "@interactjs/types": {
+      "version": "1.10.17"
+    },
+    "@interactjs/utils": {
+      "version": "1.10.17"
+    },
+    "@intlify/core-base": {
+      "version": "9.2.2",
+      "requires": {
+        "@intlify/devtools-if": "9.2.2",
+        "@intlify/message-compiler": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2"
+      }
+    },
+    "@intlify/devtools-if": {
+      "version": "9.2.2",
+      "requires": {
+        "@intlify/shared": "9.2.2"
+      }
+    },
+    "@intlify/message-compiler": {
+      "version": "9.2.2",
+      "requires": {
+        "@intlify/shared": "9.2.2",
+        "source-map": "0.6.1"
+      }
+    },
+    "@intlify/shared": {
+      "version": "9.2.2"
+    },
+    "@intlify/vue-devtools": {
+      "version": "9.2.2",
+      "requires": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2"
+      }
+    },
+    "@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      }
+    },
+    "@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "dev": true
+    },
+    "@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      }
+    },
+    "@popperjs/core": {
+      "version": "npm:@sxzz/popperjs-es@2.11.7"
+    },
+    "@transloadit/prettier-bytes": {
+      "version": "0.0.7"
+    },
+    "@types/event-emitter": {
+      "version": "0.3.3"
+    },
+    "@types/json-schema": {
+      "version": "7.0.11",
+      "dev": true
+    },
+    "@types/lodash": {
+      "version": "4.14.191"
+    },
+    "@types/lodash-es": {
+      "version": "4.17.6",
+      "requires": {
+        "@types/lodash": "*"
+      }
+    },
+    "@types/node": {
+      "version": "18.11.15",
+      "dev": true
+    },
+    "@types/nprogress": {
+      "version": "0.2.0",
+      "dev": true
+    },
+    "@types/parse-json": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "@types/semver": {
+      "version": "7.3.13",
+      "dev": true
+    },
+    "@types/sortablejs": {
+      "version": "1.15.0",
+      "dev": true
+    },
+    "@types/swagger-schema-official": {
+      "version": "2.0.22",
+      "dev": true
+    },
+    "@types/web-bluetooth": {
+      "version": "0.0.16"
+    },
+    "@typescript-eslint/eslint-plugin": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/scope-manager": "5.46.1",
+        "@typescript-eslint/type-utils": "5.46.1",
+        "@typescript-eslint/utils": "5.46.1",
+        "debug": "^4.3.4",
+        "ignore": "^5.2.0",
+        "natural-compare-lite": "^1.4.0",
+        "regexpp": "^3.2.0",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      }
+    },
+    "@typescript-eslint/parser": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/scope-manager": "5.46.1",
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/typescript-estree": "5.46.1",
+        "debug": "^4.3.4"
+      }
+    },
+    "@typescript-eslint/scope-manager": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/visitor-keys": "5.46.1"
+      }
+    },
+    "@typescript-eslint/type-utils": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/typescript-estree": "5.46.1",
+        "@typescript-eslint/utils": "5.46.1",
+        "debug": "^4.3.4",
+        "tsutils": "^3.21.0"
+      }
+    },
+    "@typescript-eslint/types": {
+      "version": "5.46.1",
+      "dev": true
+    },
+    "@typescript-eslint/typescript-estree": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/visitor-keys": "5.46.1",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "semver": "^7.3.7",
+        "tsutils": "^3.21.0"
+      }
+    },
+    "@typescript-eslint/utils": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@types/json-schema": "^7.0.9",
+        "@types/semver": "^7.3.12",
+        "@typescript-eslint/scope-manager": "5.46.1",
+        "@typescript-eslint/types": "5.46.1",
+        "@typescript-eslint/typescript-estree": "5.46.1",
+        "eslint-scope": "^5.1.1",
+        "eslint-utils": "^3.0.0",
+        "semver": "^7.3.7"
+      }
+    },
+    "@typescript-eslint/visitor-keys": {
+      "version": "5.46.1",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "5.46.1",
+        "eslint-visitor-keys": "^3.3.0"
+      }
+    },
+    "@uppy/companion-client": {
+      "version": "2.2.2",
+      "requires": {
+        "@uppy/utils": "^4.1.2",
+        "namespace-emitter": "^2.0.1"
+      }
+    },
+    "@uppy/core": {
+      "version": "2.3.4",
+      "requires": {
+        "@transloadit/prettier-bytes": "0.0.7",
+        "@uppy/store-default": "^2.1.1",
+        "@uppy/utils": "^4.1.3",
+        "lodash.throttle": "^4.1.1",
+        "mime-match": "^1.0.2",
+        "namespace-emitter": "^2.0.1",
+        "nanoid": "^3.1.25",
+        "preact": "^10.5.13"
+      }
+    },
+    "@uppy/store-default": {
+      "version": "2.1.1"
+    },
+    "@uppy/utils": {
+      "version": "4.1.3",
+      "requires": {
+        "lodash.throttle": "^4.1.1"
+      }
+    },
+    "@uppy/xhr-upload": {
+      "version": "2.1.3",
+      "requires": {
+        "@uppy/companion-client": "^2.2.2",
+        "@uppy/utils": "^4.1.2",
+        "nanoid": "^3.1.25"
+      }
+    },
+    "@vitejs/plugin-vue": {
+      "version": "4.0.0",
+      "dev": true,
+      "requires": {}
+    },
+    "@vue/compiler-core": {
+      "version": "3.2.45",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/shared": "3.2.45",
+        "estree-walker": "^2.0.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/compiler-core": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.2.45",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.45",
+        "@vue/compiler-dom": "3.2.45",
+        "@vue/compiler-ssr": "3.2.45",
+        "@vue/reactivity-transform": "3.2.45",
+        "@vue/shared": "3.2.45",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7",
+        "postcss": "^8.1.10",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/compiler-dom": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "@vue/devtools-api": {
+      "version": "6.4.5"
+    },
+    "@vue/reactivity": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "@vue/reactivity-transform": {
+      "version": "3.2.45",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.45",
+        "@vue/shared": "3.2.45",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/reactivity": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/runtime-core": "3.2.45",
+        "@vue/shared": "3.2.45",
+        "csstype": "^2.6.8"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/compiler-ssr": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.2.45"
+    },
+    "@vueuse/core": {
+      "version": "9.6.0",
+      "requires": {
+        "@types/web-bluetooth": "^0.0.16",
+        "@vueuse/metadata": "9.6.0",
+        "@vueuse/shared": "9.6.0",
+        "vue-demi": "*"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.13.11",
+          "requires": {}
+        }
+      }
+    },
+    "@vueuse/metadata": {
+      "version": "9.6.0"
+    },
+    "@vueuse/shared": {
+      "version": "9.6.0",
+      "requires": {
+        "vue-demi": "*"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.13.11",
+          "requires": {}
+        }
+      }
+    },
+    "@wangeditor/basic-modules": {
+      "version": "1.1.7",
+      "requires": {
+        "is-url": "^1.2.4"
+      }
+    },
+    "@wangeditor/code-highlight": {
+      "version": "1.0.3",
+      "requires": {
+        "prismjs": "^1.23.0"
+      }
+    },
+    "@wangeditor/core": {
+      "version": "1.1.19",
+      "requires": {
+        "@types/event-emitter": "^0.3.3",
+        "event-emitter": "^0.3.5",
+        "html-void-elements": "^2.0.0",
+        "i18next": "^20.4.0",
+        "scroll-into-view-if-needed": "^2.2.28",
+        "slate-history": "^0.66.0"
+      }
+    },
+    "@wangeditor/editor": {
+      "version": "5.1.23",
+      "requires": {
+        "@uppy/core": "^2.1.1",
+        "@uppy/xhr-upload": "^2.0.3",
+        "@wangeditor/basic-modules": "^1.1.7",
+        "@wangeditor/code-highlight": "^1.0.3",
+        "@wangeditor/core": "^1.1.19",
+        "@wangeditor/list-module": "^1.0.5",
+        "@wangeditor/table-module": "^1.1.4",
+        "@wangeditor/upload-image-module": "^1.0.2",
+        "@wangeditor/video-module": "^1.1.4",
+        "dom7": "^3.0.0",
+        "is-hotkey": "^0.2.0",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.clonedeep": "^4.5.0",
+        "lodash.debounce": "^4.0.8",
+        "lodash.foreach": "^4.5.0",
+        "lodash.isequal": "^4.5.0",
+        "lodash.throttle": "^4.1.1",
+        "lodash.toarray": "^4.4.0",
+        "nanoid": "^3.2.0",
+        "slate": "^0.72.0",
+        "snabbdom": "^3.1.0"
+      }
+    },
+    "@wangeditor/editor-for-vue": {
+      "version": "5.1.12",
+      "requires": {}
+    },
+    "@wangeditor/list-module": {
+      "version": "1.0.5",
+      "requires": {}
+    },
+    "@wangeditor/table-module": {
+      "version": "1.1.4",
+      "requires": {}
+    },
+    "@wangeditor/upload-image-module": {
+      "version": "1.0.2",
+      "requires": {}
+    },
+    "@wangeditor/video-module": {
+      "version": "1.1.4",
+      "requires": {}
+    },
+    "acorn": {
+      "version": "8.8.1",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "dev": true,
+      "requires": {}
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-regex": {
+      "version": "5.0.1",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "4.3.0",
+      "dev": true,
+      "requires": {
+        "color-convert": "^2.0.1"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.3",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "argparse": {
+      "version": "2.0.1",
+      "dev": true
+    },
+    "array-union": {
+      "version": "2.1.0",
+      "dev": true
+    },
+    "async-validator": {
+      "version": "4.2.5"
+    },
+    "asynckit": {
+      "version": "0.4.0"
+    },
+    "axios": {
+      "version": "1.2.1",
+      "requires": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "dev": true
+    },
+    "batch-processor": {
+      "version": "1.0.0"
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "dev": true
+    },
+    "boolbase": {
+      "version": "1.0.0",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "call-me-maybe": {
+      "version": "1.0.2",
+      "dev": true
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "dev": true
+    },
+    "chalk": {
+      "version": "4.1.2",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      }
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "5.1.2",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        }
+      }
+    },
+    "claygl": {
+      "version": "1.3.0"
+    },
+    "clipboard": {
+      "version": "2.0.11",
+      "requires": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "cliui": {
+      "version": "8.0.1",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "2.0.1",
+      "dev": true,
+      "requires": {
+        "color-name": "~1.1.4"
+      }
+    },
+    "color-name": {
+      "version": "1.1.4",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "compute-scroll-into-view": {
+      "version": "1.0.20"
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "dev": true
+    },
+    "cosmiconfig": {
+      "version": "7.0.1",
+      "dev": true,
+      "requires": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      }
+    },
+    "countup.js": {
+      "version": "2.3.2"
+    },
+    "cropperjs": {
+      "version": "1.5.13"
+    },
+    "cross-spawn": {
+      "version": "7.0.3",
+      "dev": true,
+      "requires": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      }
+    },
+    "cssesc": {
+      "version": "3.0.0",
+      "dev": true
+    },
+    "csstype": {
+      "version": "2.6.21"
+    },
+    "d": {
+      "version": "1.0.1",
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "data-uri-to-buffer": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "dayjs": {
+      "version": "1.11.7"
+    },
+    "debug": {
+      "version": "4.3.4",
+      "dev": true,
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "deep-is": {
+      "version": "0.1.4",
+      "dev": true
+    },
+    "delayed-stream": {
+      "version": "1.0.0"
+    },
+    "delegate": {
+      "version": "3.2.0"
+    },
+    "didyoumean": {
+      "version": "1.2.2",
+      "dev": true
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      }
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "dom7": {
+      "version": "3.0.0",
+      "requires": {
+        "ssr-window": "^3.0.0-alpha.1"
+      }
+    },
+    "dotenv": {
+      "version": "16.0.3",
+      "dev": true
+    },
+    "echarts": {
+      "version": "5.4.1",
+      "requires": {
+        "tslib": "2.3.0",
+        "zrender": "5.4.1"
+      }
+    },
+    "echarts-gl": {
+      "version": "2.0.9",
+      "requires": {
+        "claygl": "^1.2.1",
+        "zrender": "^5.1.1"
+      }
+    },
+    "echarts-wordcloud": {
+      "version": "2.1.0",
+      "requires": {}
+    },
+    "element-plus": {
+      "version": "2.2.26",
+      "requires": {
+        "@ctrl/tinycolor": "^3.4.1",
+        "@element-plus/icons-vue": "^2.0.6",
+        "@floating-ui/dom": "^1.0.1",
+        "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+        "@types/lodash": "^4.14.182",
+        "@types/lodash-es": "^4.17.6",
+        "@vueuse/core": "^9.1.0",
+        "async-validator": "^4.2.5",
+        "dayjs": "^1.11.3",
+        "escape-html": "^1.0.3",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "lodash-unified": "^1.0.2",
+        "memoize-one": "^6.0.0",
+        "normalize-wheel-es": "^1.2.0"
+      }
+    },
+    "element-resize-detector": {
+      "version": "1.2.4",
+      "requires": {
+        "batch-processor": "1.0.0"
+      }
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "dev": true
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es5-ext": {
+      "version": "0.10.62",
+      "requires": {
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.3",
+        "next-tick": "^1.1.0"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-promise": {
+      "version": "3.3.1",
+      "dev": true
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "esbuild": {
+      "version": "0.16.6",
+      "dev": true,
+      "requires": {
+        "@esbuild/android-arm": "0.16.6",
+        "@esbuild/android-arm64": "0.16.6",
+        "@esbuild/android-x64": "0.16.6",
+        "@esbuild/darwin-arm64": "0.16.6",
+        "@esbuild/darwin-x64": "0.16.6",
+        "@esbuild/freebsd-arm64": "0.16.6",
+        "@esbuild/freebsd-x64": "0.16.6",
+        "@esbuild/linux-arm": "0.16.6",
+        "@esbuild/linux-arm64": "0.16.6",
+        "@esbuild/linux-ia32": "0.16.6",
+        "@esbuild/linux-loong64": "0.16.6",
+        "@esbuild/linux-mips64el": "0.16.6",
+        "@esbuild/linux-ppc64": "0.16.6",
+        "@esbuild/linux-riscv64": "0.16.6",
+        "@esbuild/linux-s390x": "0.16.6",
+        "@esbuild/linux-x64": "0.16.6",
+        "@esbuild/netbsd-x64": "0.16.6",
+        "@esbuild/openbsd-x64": "0.16.6",
+        "@esbuild/sunos-x64": "0.16.6",
+        "@esbuild/win32-arm64": "0.16.6",
+        "@esbuild/win32-ia32": "0.16.6",
+        "@esbuild/win32-x64": "0.16.6"
+      }
+    },
+    "escalade": {
+      "version": "3.1.1",
+      "dev": true
+    },
+    "escape-html": {
+      "version": "1.0.3"
+    },
+    "escape-string-regexp": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "eslint": {
+      "version": "8.29.0",
+      "dev": true,
+      "requires": {
+        "@eslint/eslintrc": "^1.3.3",
+        "@humanwhocodes/config-array": "^0.11.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "ajv": "^6.10.0",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.1.1",
+        "eslint-utils": "^3.0.0",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.4.0",
+        "esquery": "^1.4.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.15.0",
+        "grapheme-splitter": "^1.0.4",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-sdsl": "^4.1.4",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.1",
+        "regexpp": "^3.2.0",
+        "strip-ansi": "^6.0.1",
+        "strip-json-comments": "^3.1.0",
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "eslint-scope": {
+          "version": "7.1.1",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.3.0",
+            "estraverse": "^5.2.0"
+          }
+        },
+        "estraverse": {
+          "version": "5.3.0",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-vue": {
+      "version": "9.8.0",
+      "dev": true,
+      "requires": {
+        "eslint-utils": "^3.0.0",
+        "natural-compare": "^1.4.0",
+        "nth-check": "^2.0.1",
+        "postcss-selector-parser": "^6.0.9",
+        "semver": "^7.3.5",
+        "vue-eslint-parser": "^9.0.1",
+        "xml-name-validator": "^4.0.0"
+      }
+    },
+    "eslint-scope": {
+      "version": "5.1.1",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      }
+    },
+    "eslint-utils": {
+      "version": "3.0.0",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^2.0.0"
+      },
+      "dependencies": {
+        "eslint-visitor-keys": {
+          "version": "2.1.0",
+          "dev": true
+        }
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "3.3.0",
+      "dev": true
+    },
+    "espree": {
+      "version": "9.4.1",
+      "dev": true,
+      "requires": {
+        "acorn": "^8.8.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.3.0"
+      }
+    },
+    "esquery": {
+      "version": "1.4.0",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.1.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.3.0",
+          "dev": true
+        }
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      },
+      "dependencies": {
+        "estraverse": {
+          "version": "5.3.0",
+          "dev": true
+        }
+      }
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "dev": true
+    },
+    "estree-walker": {
+      "version": "2.0.2"
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "dev": true
+    },
+    "eta": {
+      "version": "1.12.3",
+      "dev": true
+    },
+    "event-emitter": {
+      "version": "0.3.5",
+      "requires": {
+        "d": "1",
+        "es5-ext": "~0.10.14"
+      }
+    },
+    "ext": {
+      "version": "1.7.0",
+      "requires": {
+        "type": "^2.7.2"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.7.2"
+        }
+      }
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "dev": true
+    },
+    "fast-glob": {
+      "version": "3.2.12",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      },
+      "dependencies": {
+        "glob-parent": {
+          "version": "5.1.2",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.1"
+          }
+        }
+      }
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "dev": true
+    },
+    "fast-safe-stringify": {
+      "version": "2.1.1",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.14.0",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "fetch-blob": {
+      "version": "3.2.0",
+      "dev": true,
+      "requires": {
+        "node-domexception": "^1.0.0",
+        "web-streams-polyfill": "^3.0.3"
+      }
+    },
+    "file-entry-cache": {
+      "version": "6.0.1",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^3.0.4"
+      }
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "find-up": {
+      "version": "5.0.0",
+      "dev": true,
+      "requires": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
+    "flat-cache": {
+      "version": "3.0.4",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.1.0",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "flatted": {
+      "version": "3.2.7",
+      "dev": true
+    },
+    "follow-redirects": {
+      "version": "1.15.2"
+    },
+    "form-data": {
+      "version": "4.0.0",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "formdata-polyfill": {
+      "version": "4.0.10",
+      "dev": true,
+      "requires": {
+        "fetch-blob": "^3.1.2"
+      }
+    },
+    "fs-extra": {
+      "version": "10.1.0",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "dev": true
+    },
+    "function-bind": {
+      "version": "1.1.1"
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "dev": true
+    },
+    "get-intrinsic": {
+      "version": "1.1.3",
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.3"
+      }
+    },
+    "glob": {
+      "version": "7.2.3",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "6.0.2",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.3"
+      }
+    },
+    "globals": {
+      "version": "13.19.0",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.20.2"
+      }
+    },
+    "globby": {
+      "version": "11.1.0",
+      "dev": true,
+      "requires": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      }
+    },
+    "good-listener": {
+      "version": "1.2.2",
+      "requires": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.10",
+      "dev": true
+    },
+    "grapheme-splitter": {
+      "version": "1.0.4",
+      "dev": true
+    },
+    "has": {
+      "version": "1.0.3",
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-flag": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "has-symbols": {
+      "version": "1.0.3"
+    },
+    "html-void-elements": {
+      "version": "2.0.1"
+    },
+    "http2-client": {
+      "version": "1.3.5",
+      "dev": true
+    },
+    "i18next": {
+      "version": "20.6.1",
+      "requires": {
+        "@babel/runtime": "^7.12.0"
+      }
+    },
+    "ignore": {
+      "version": "5.2.1",
+      "dev": true
+    },
+    "immer": {
+      "version": "9.0.16"
+    },
+    "immutable": {
+      "version": "4.1.0",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.3.0",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "dev": true
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-core-module": {
+      "version": "2.11.0",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-hotkey": {
+      "version": "0.2.0"
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "dev": true
+    },
+    "is-path-inside": {
+      "version": "3.0.3",
+      "dev": true
+    },
+    "is-plain-object": {
+      "version": "5.0.0"
+    },
+    "is-url": {
+      "version": "1.2.4"
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "dev": true
+    },
+    "js-cookie": {
+      "version": "3.0.1"
+    },
+    "js-sdsl": {
+      "version": "4.2.0",
+      "dev": true
+    },
+    "js-table2excel": {
+      "version": "1.0.3"
+    },
+    "js-tokens": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "js-yaml": {
+      "version": "4.1.0",
+      "dev": true,
+      "requires": {
+        "argparse": "^2.0.1"
+      }
+    },
+    "json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "dev": true
+    },
+    "jsonfile": {
+      "version": "6.1.0",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.6",
+        "universalify": "^2.0.0"
+      }
+    },
+    "jsplumb": {
+      "version": "2.15.6"
+    },
+    "levn": {
+      "version": "0.4.1",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      }
+    },
+    "lines-and-columns": {
+      "version": "1.2.4",
+      "dev": true
+    },
+    "locate-path": {
+      "version": "6.0.0",
+      "dev": true,
+      "requires": {
+        "p-locate": "^5.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.21"
+    },
+    "lodash-es": {
+      "version": "4.17.21"
+    },
+    "lodash-unified": {
+      "version": "1.0.3",
+      "requires": {}
+    },
+    "lodash.camelcase": {
+      "version": "4.3.0"
+    },
+    "lodash.clonedeep": {
+      "version": "4.5.0"
+    },
+    "lodash.debounce": {
+      "version": "4.0.8"
+    },
+    "lodash.foreach": {
+      "version": "4.5.0"
+    },
+    "lodash.isequal": {
+      "version": "4.5.0"
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "dev": true
+    },
+    "lodash.throttle": {
+      "version": "4.1.1"
+    },
+    "lodash.toarray": {
+      "version": "4.4.0"
+    },
+    "lru-cache": {
+      "version": "6.0.0",
+      "dev": true,
+      "requires": {
+        "yallist": "^4.0.0"
+      }
+    },
+    "magic-string": {
+      "version": "0.25.9",
+      "requires": {
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "make-dir": {
+      "version": "3.1.0",
+      "dev": true,
+      "requires": {
+        "semver": "^6.0.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "dev": true
+        }
+      }
+    },
+    "memoize-one": {
+      "version": "6.0.0"
+    },
+    "merge2": {
+      "version": "1.4.1",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "4.0.5",
+      "dev": true,
+      "requires": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      }
+    },
+    "mime-db": {
+      "version": "1.52.0"
+    },
+    "mime-match": {
+      "version": "1.0.2",
+      "requires": {
+        "wildcard": "^1.1.0"
+      }
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    },
+    "minimatch": {
+      "version": "3.1.2",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^1.1.7"
+      }
+    },
+    "mitt": {
+      "version": "3.0.0"
+    },
+    "ms": {
+      "version": "2.1.2",
+      "dev": true
+    },
+    "namespace-emitter": {
+      "version": "2.0.1"
+    },
+    "nanoid": {
+      "version": "3.3.4"
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "dev": true
+    },
+    "natural-compare-lite": {
+      "version": "1.4.0",
+      "dev": true
+    },
+    "next-tick": {
+      "version": "1.1.0"
+    },
+    "node-domexception": {
+      "version": "1.0.0",
+      "dev": true
+    },
+    "node-emoji": {
+      "version": "1.11.0",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.21"
+      }
+    },
+    "node-fetch": {
+      "version": "3.3.0",
+      "dev": true,
+      "requires": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      }
+    },
+    "node-fetch-h2": {
+      "version": "2.3.0",
+      "dev": true,
+      "requires": {
+        "http2-client": "^1.2.5"
+      }
+    },
+    "node-readfiles": {
+      "version": "0.2.0",
+      "dev": true,
+      "requires": {
+        "es6-promise": "^3.2.1"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "dev": true
+    },
+    "normalize-wheel-es": {
+      "version": "1.2.0"
+    },
+    "nprogress": {
+      "version": "0.2.0"
+    },
+    "nth-check": {
+      "version": "2.1.1",
+      "dev": true,
+      "requires": {
+        "boolbase": "^1.0.0"
+      }
+    },
+    "oas-kit-common": {
+      "version": "1.0.8",
+      "dev": true,
+      "requires": {
+        "fast-safe-stringify": "^2.0.7"
+      }
+    },
+    "oas-linter": {
+      "version": "3.2.2",
+      "dev": true,
+      "requires": {
+        "@exodus/schemasafe": "^1.0.0-rc.2",
+        "should": "^13.2.1",
+        "yaml": "^1.10.0"
+      }
+    },
+    "oas-resolver": {
+      "version": "2.5.6",
+      "dev": true,
+      "requires": {
+        "node-fetch-h2": "^2.3.0",
+        "oas-kit-common": "^1.0.8",
+        "reftools": "^1.1.9",
+        "yaml": "^1.10.0",
+        "yargs": "^17.0.1"
+      }
+    },
+    "oas-schema-walker": {
+      "version": "1.1.5",
+      "dev": true
+    },
+    "oas-validator": {
+      "version": "5.0.8",
+      "dev": true,
+      "requires": {
+        "call-me-maybe": "^1.0.1",
+        "oas-kit-common": "^1.0.8",
+        "oas-linter": "^3.2.2",
+        "oas-resolver": "^2.5.6",
+        "oas-schema-walker": "^1.1.5",
+        "reftools": "^1.1.9",
+        "should": "^13.2.1",
+        "yaml": "^1.10.0"
+      }
+    },
+    "object-inspect": {
+      "version": "1.12.2"
+    },
+    "once": {
+      "version": "1.4.0",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "optionator": {
+      "version": "0.9.1",
+      "dev": true,
+      "requires": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.3"
+      }
+    },
+    "p-limit": {
+      "version": "3.1.0",
+      "dev": true,
+      "requires": {
+        "yocto-queue": "^0.1.0"
+      }
+    },
+    "p-locate": {
+      "version": "5.0.0",
+      "dev": true,
+      "requires": {
+        "p-limit": "^3.0.2"
+      }
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "parse-json": {
+      "version": "5.2.0",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      }
+    },
+    "path-exists": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "dev": true
+    },
+    "path-key": {
+      "version": "3.1.1",
+      "dev": true
+    },
+    "path-parse": {
+      "version": "1.0.7",
+      "dev": true
+    },
+    "path-type": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "picocolors": {
+      "version": "1.0.0"
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "dev": true
+    },
+    "pinia": {
+      "version": "2.0.28",
+      "requires": {
+        "@vue/devtools-api": "^6.4.5",
+        "vue-demi": "*"
+      },
+      "dependencies": {
+        "vue-demi": {
+          "version": "0.13.11",
+          "requires": {}
+        }
+      }
+    },
+    "postcss": {
+      "version": "8.4.20",
+      "requires": {
+        "nanoid": "^3.3.4",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "postcss-selector-parser": {
+      "version": "6.0.11",
+      "dev": true,
+      "requires": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      }
+    },
+    "preact": {
+      "version": "10.11.3"
+    },
+    "prelude-ls": {
+      "version": "1.2.1",
+      "dev": true
+    },
+    "prettier": {
+      "version": "2.8.1",
+      "dev": true
+    },
+    "print-js": {
+      "version": "1.6.0"
+    },
+    "prismjs": {
+      "version": "1.29.0"
+    },
+    "proxy-from-env": {
+      "version": "1.1.0"
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "dev": true
+    },
+    "qrcodejs2-fixes": {
+      "version": "0.0.2"
+    },
+    "qs": {
+      "version": "6.11.0",
+      "requires": {
+        "side-channel": "^1.0.4"
+      }
+    },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "dev": true
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "reftools": {
+      "version": "1.1.9",
+      "dev": true
+    },
+    "regenerator-runtime": {
+      "version": "0.13.11"
+    },
+    "regexpp": {
+      "version": "3.2.0",
+      "dev": true
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.22.1",
+      "dev": true,
+      "requires": {
+        "is-core-module": "^2.9.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      }
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "reusify": {
+      "version": "1.0.4",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "rollup": {
+      "version": "3.7.4",
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "run-parallel": {
+      "version": "1.2.0",
+      "dev": true,
+      "requires": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "sass": {
+      "version": "1.56.2",
+      "dev": true,
+      "requires": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      }
+    },
+    "screenfull": {
+      "version": "6.0.2"
+    },
+    "scroll-into-view-if-needed": {
+      "version": "2.2.31",
+      "requires": {
+        "compute-scroll-into-view": "^1.0.20"
+      }
+    },
+    "select": {
+      "version": "1.1.2"
+    },
+    "semver": {
+      "version": "7.3.8",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^6.0.0"
+      }
+    },
+    "shebang-command": {
+      "version": "2.0.0",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^3.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "3.0.0",
+      "dev": true
+    },
+    "should": {
+      "version": "13.2.3",
+      "dev": true,
+      "requires": {
+        "should-equal": "^2.0.0",
+        "should-format": "^3.0.3",
+        "should-type": "^1.4.0",
+        "should-type-adaptors": "^1.0.1",
+        "should-util": "^1.0.0"
+      }
+    },
+    "should-equal": {
+      "version": "2.0.0",
+      "dev": true,
+      "requires": {
+        "should-type": "^1.4.0"
+      }
+    },
+    "should-format": {
+      "version": "3.0.3",
+      "dev": true,
+      "requires": {
+        "should-type": "^1.3.0",
+        "should-type-adaptors": "^1.0.1"
+      }
+    },
+    "should-type": {
+      "version": "1.4.0",
+      "dev": true
+    },
+    "should-type-adaptors": {
+      "version": "1.1.0",
+      "dev": true,
+      "requires": {
+        "should-type": "^1.3.0",
+        "should-util": "^1.0.0"
+      }
+    },
+    "should-util": {
+      "version": "1.0.1",
+      "dev": true
+    },
+    "side-channel": {
+      "version": "1.0.4",
+      "requires": {
+        "call-bind": "^1.0.0",
+        "get-intrinsic": "^1.0.2",
+        "object-inspect": "^1.9.0"
+      }
+    },
+    "slash": {
+      "version": "3.0.0",
+      "dev": true
+    },
+    "slate": {
+      "version": "0.72.8",
+      "requires": {
+        "immer": "^9.0.6",
+        "is-plain-object": "^5.0.0",
+        "tiny-warning": "^1.0.3"
+      }
+    },
+    "slate-history": {
+      "version": "0.66.0",
+      "requires": {
+        "is-plain-object": "^5.0.0"
+      }
+    },
+    "snabbdom": {
+      "version": "3.5.1"
+    },
+    "sortablejs": {
+      "version": "1.15.0"
+    },
+    "source-map": {
+      "version": "0.6.1"
+    },
+    "source-map-js": {
+      "version": "1.0.2"
+    },
+    "sourcemap-codec": {
+      "version": "1.4.8"
+    },
+    "splitpanes": {
+      "version": "3.1.5"
+    },
+    "ssr-window": {
+      "version": "3.0.0"
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.1",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "7.2.0",
+      "dev": true,
+      "requires": {
+        "has-flag": "^4.0.0"
+      }
+    },
+    "supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "dev": true
+    },
+    "swagger-schema-official": {
+      "version": "2.0.0-bab6bed",
+      "dev": true
+    },
+    "swagger-typescript-api": {
+      "version": "12.0.2",
+      "dev": true,
+      "requires": {
+        "@types/swagger-schema-official": "2.0.22",
+        "cosmiconfig": "7.0.1",
+        "didyoumean": "^1.2.2",
+        "eta": "1.12.3",
+        "js-yaml": "4.1.0",
+        "lodash": "4.17.21",
+        "make-dir": "3.1.0",
+        "nanoid": "3.3.4",
+        "node-emoji": "1.11.0",
+        "node-fetch": "^3.2.10",
+        "prettier": "2.7.1",
+        "swagger-schema-official": "2.0.0-bab6bed",
+        "swagger2openapi": "7.0.8",
+        "typescript": "4.8.4"
+      },
+      "dependencies": {
+        "prettier": {
+          "version": "2.7.1",
+          "dev": true
+        },
+        "typescript": {
+          "version": "4.8.4",
+          "dev": true
+        }
+      }
+    },
+    "swagger2openapi": {
+      "version": "7.0.8",
+      "dev": true,
+      "requires": {
+        "call-me-maybe": "^1.0.1",
+        "node-fetch": "^2.6.1",
+        "node-fetch-h2": "^2.3.0",
+        "node-readfiles": "^0.2.0",
+        "oas-kit-common": "^1.0.8",
+        "oas-resolver": "^2.5.6",
+        "oas-schema-walker": "^1.1.5",
+        "oas-validator": "^5.0.8",
+        "reftools": "^1.1.9",
+        "yaml": "^1.10.0",
+        "yargs": "^17.0.1"
+      },
+      "dependencies": {
+        "node-fetch": {
+          "version": "2.6.7",
+          "dev": true,
+          "requires": {
+            "whatwg-url": "^5.0.0"
+          }
+        }
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "dev": true
+    },
+    "tiny-emitter": {
+      "version": "2.1.0"
+    },
+    "tiny-warning": {
+      "version": "1.0.3"
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "tr46": {
+      "version": "0.0.3",
+      "dev": true
+    },
+    "tslib": {
+      "version": "2.3.0"
+    },
+    "tsutils": {
+      "version": "3.21.0",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.8.1"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "1.14.1",
+          "dev": true
+        }
+      }
+    },
+    "type": {
+      "version": "1.2.0"
+    },
+    "type-check": {
+      "version": "0.4.0",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1"
+      }
+    },
+    "type-fest": {
+      "version": "0.20.2",
+      "dev": true
+    },
+    "typescript": {
+      "version": "4.9.4",
+      "devOptional": true
+    },
+    "universalify": {
+      "version": "2.0.0",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "dev": true
+    },
+    "vite": {
+      "version": "4.0.1",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.16.3",
+        "fsevents": "~2.3.2",
+        "postcss": "^8.4.20",
+        "resolve": "^1.22.1",
+        "rollup": "^3.7.0"
+      }
+    },
+    "vite-plugin-compression": {
+      "version": "0.5.1",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.2",
+        "debug": "^4.3.3",
+        "fs-extra": "^10.0.0"
+      }
+    },
+    "vite-plugin-vue-setup-extend": {
+      "version": "0.4.0",
+      "dev": true,
+      "requires": {
+        "@vue/compiler-sfc": "^3.2.29",
+        "magic-string": "^0.25.7"
+      }
+    },
+    "vue": {
+      "version": "3.2.45",
+      "requires": {
+        "@vue/compiler-dom": "3.2.45",
+        "@vue/compiler-sfc": "3.2.45",
+        "@vue/runtime-dom": "3.2.45",
+        "@vue/server-renderer": "3.2.45",
+        "@vue/shared": "3.2.45"
+      }
+    },
+    "vue-clipboard3": {
+      "version": "2.0.0",
+      "requires": {
+        "clipboard": "^2.0.6"
+      }
+    },
+    "vue-eslint-parser": {
+      "version": "9.1.0",
+      "dev": true,
+      "requires": {
+        "debug": "^4.3.4",
+        "eslint-scope": "^7.1.1",
+        "eslint-visitor-keys": "^3.3.0",
+        "espree": "^9.3.1",
+        "esquery": "^1.4.0",
+        "lodash": "^4.17.21",
+        "semver": "^7.3.6"
+      },
+      "dependencies": {
+        "eslint-scope": {
+          "version": "7.1.1",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.3.0",
+            "estraverse": "^5.2.0"
+          }
+        },
+        "estraverse": {
+          "version": "5.3.0",
+          "dev": true
+        }
+      }
+    },
+    "vue-grid-layout": {
+      "version": "3.0.0-beta1",
+      "requires": {
+        "@interactjs/actions": "^1.10.2",
+        "@interactjs/auto-start": "^1.10.2",
+        "@interactjs/dev-tools": "^1.10.2",
+        "@interactjs/interactjs": "^1.10.2",
+        "@interactjs/modifiers": "^1.10.2",
+        "element-resize-detector": "^1.2.1",
+        "mitt": "^2.1.0"
+      },
+      "dependencies": {
+        "mitt": {
+          "version": "2.1.0"
+        }
+      }
+    },
+    "vue-i18n": {
+      "version": "9.2.2",
+      "requires": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      }
+    },
+    "vue-router": {
+      "version": "4.1.6",
+      "requires": {
+        "@vue/devtools-api": "^6.4.5"
+      }
+    },
+    "web-streams-polyfill": {
+      "version": "3.2.1",
+      "dev": true
+    },
+    "webidl-conversions": {
+      "version": "3.0.1",
+      "dev": true
+    },
+    "whatwg-url": {
+      "version": "5.0.0",
+      "dev": true,
+      "requires": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "which": {
+      "version": "2.0.2",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "wildcard": {
+      "version": "1.1.2"
+    },
+    "word-wrap": {
+      "version": "1.2.3",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "7.0.0",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "dev": true
+    },
+    "xml-name-validator": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "y18n": {
+      "version": "5.0.8",
+      "dev": true
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "dev": true
+    },
+    "yaml": {
+      "version": "1.10.2",
+      "dev": true
+    },
+    "yargs": {
+      "version": "17.6.2",
+      "dev": true,
+      "requires": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      }
+    },
+    "yargs-parser": {
+      "version": "21.1.1",
+      "dev": true
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "dev": true
+    },
+    "zrender": {
+      "version": "5.4.1",
+      "requires": {
+        "tslib": "2.3.0"
+      }
+    }
+  }
+}

+ 84 - 0
package.json

@@ -0,0 +1,84 @@
+{
+  "name": "admin.ui.plus",
+  "version": "1.0.0",
+  "description": "vue3 vite admin plus",
+  "author": "",
+  "license": "MIT",
+  "scripts": {
+    "dev": "vite --force",
+    "build": "vite build",
+    "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/",
+    "format": "npx prettier --write .",
+    "install:pkg": "npm install --registry https://registry.npmmirror.com",
+    "gen:admin-api": "node ./gen/gen-admin-api"
+  },
+  "dependencies": {
+    "@element-plus/icons-vue": "^2.0.10",
+    "@wangeditor/editor": "^5.1.23",
+    "@wangeditor/editor-for-vue": "^5.1.12",
+    "axios": "^1.2.1",
+    "countup.js": "^2.3.2",
+    "cropperjs": "^1.5.13",
+    "echarts": "^5.4.1",
+    "echarts-gl": "^2.0.9",
+    "echarts-wordcloud": "^2.1.0",
+    "element-plus": "^2.2.26",
+    "js-cookie": "^3.0.1",
+    "js-table2excel": "^1.0.3",
+    "jsplumb": "^2.15.6",
+    "mitt": "^3.0.0",
+    "nprogress": "^0.2.0",
+    "pinia": "^2.0.28",
+    "print-js": "^1.6.0",
+    "qrcodejs2-fixes": "^0.0.2",
+    "qs": "^6.11.0",
+    "screenfull": "^6.0.2",
+    "sortablejs": "^1.15.0",
+    "splitpanes": "^3.1.5",
+    "vue": "^3.2.45",
+    "vue-clipboard3": "^2.0.0",
+    "vue-grid-layout": "^3.0.0-beta1",
+    "vue-i18n": "^9.2.2",
+    "vue-router": "^4.1.6"
+  },
+  "devDependencies": {
+    "@types/node": "^18.11.13",
+    "@types/nprogress": "^0.2.0",
+    "@types/sortablejs": "^1.15.0",
+    "@typescript-eslint/eslint-plugin": "^5.46.0",
+    "@typescript-eslint/parser": "^5.46.0",
+    "@vitejs/plugin-vue": "^4.0.0",
+    "@vue/compiler-sfc": "^3.2.45",
+    "eslint": "^8.29.0",
+    "eslint-plugin-vue": "^9.8.0",
+    "prettier": "^2.8.1",
+    "sass": "^1.56.2",
+    "typescript": "^4.9.4",
+    "vite": "^4.0.0",
+    "vite-plugin-vue-setup-extend": "^0.4.0",
+    "vue-eslint-parser": "^9.1.0",
+    "dotenv": "16.0.3",
+    "vite-plugin-compression": "0.5.1",
+    "swagger-typescript-api": "12.0.2"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ],
+  "bugs": {
+    "url": "https://github.com/zhontai/admin.ui.plus/issues"
+  },
+  "engines": {
+    "node": ">=16.0.0",
+    "npm": ">= 7.0.0"
+  },
+  "keywords": [
+    "vue3",
+    "element-plus"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/zhontai/admin.ui.plus/git"
+  }
+}

BIN
public/favicon.ico


+ 93 - 0
src/App.vue

@@ -0,0 +1,93 @@
+<template>
+  <el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
+    <router-view v-show="themeConfig.lockScreenTime > 1" />
+    <LockScreen v-if="themeConfig.isLockScreen" />
+    <Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
+    <CloseFull v-if="!themeConfig.isLockScreen" />
+    <Upgrade v-if="getVersion" />
+  </el-config-provider>
+</template>
+
+<script setup lang="ts" name="app">
+import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { useI18n } from 'vue-i18n'
+import { storeToRefs } from 'pinia'
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import other from '/@/utils/other'
+import { Local, Session } from '/@/utils/storage'
+import mittBus from '/@/utils/mitt'
+import setIntroduction from '/@/utils/setIconfont'
+
+// 引入组件
+const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'))
+const Setings = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue'))
+const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue'))
+const Upgrade = defineAsyncComponent(() => import('/@/layout/upgrade/index.vue'))
+
+// 定义变量内容
+const { messages, locale } = useI18n()
+const setingsRef = ref()
+const route = useRoute()
+const stores = useTagsViewRoutes()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+
+// 获取版本号
+const getVersion = computed(() => {
+  let isVersion = false
+  if (route.path !== '/login') {
+    // @ts-ignore
+    if ((Local.get('version') && Local.get('version') !== __VERSION__) || !Local.get('version')) isVersion = true
+  }
+  return isVersion
+})
+// 获取全局组件大小
+const getGlobalComponentSize = computed(() => {
+  return other.globalComponentSize()
+})
+// 获取全局 i18n
+const getGlobalI18n = computed(() => {
+  return messages.value[locale.value]
+})
+// 设置初始化,防止刷新时恢复默认
+onBeforeMount(() => {
+  // 设置批量第三方 icon 图标
+  setIntroduction.cssCdn()
+  // 设置批量第三方 js
+  setIntroduction.jsCdn()
+})
+// 页面加载时
+onMounted(() => {
+  nextTick(() => {
+    // 监听布局配'置弹窗点击打开
+    mittBus.on('openSetingsDrawer', () => {
+      setingsRef.value.openDrawer()
+    })
+    // 获取缓存中的布局配置
+    if (Local.get('themeConfig')) {
+      storesThemeConfig.setThemeConfig({ themeConfig: Local.get('themeConfig') })
+      document.documentElement.style.cssText = Local.get('themeConfigStyle')
+    }
+    // 获取缓存中的全屏配置
+    if (Session.get('isTagsViewCurrenFull')) {
+      stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'))
+    }
+  })
+})
+// 页面销毁时,关闭监听布局配置/i18n监听
+onUnmounted(() => {
+  mittBus.off('openSetingsDrawer', () => {})
+})
+// 监听路由的变化,设置网站标题
+watch(
+  () => route.path,
+  () => {
+    other.useTitle()
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 211 - 0
src/api/admin/Api.ts

@@ -0,0 +1,211 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  ApiAddInput,
+  ApiSyncInput,
+  ApiUpdateInput,
+  PageInputApiGetPageDto,
+  ResultOutputApiGetOutput,
+  ResultOutputInt64,
+  ResultOutputListApiListOutput,
+  ResultOutputPageOutputApiEntity,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags api
+   * @name Get
+   * @summary 查询接口
+   * @request GET:/api/admin/api/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputApiGetOutput, any>({
+      path: `/api/admin/api/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name GetList
+   * @summary 查询列表
+   * @request GET:/api/admin/api/get-list
+   * @secure
+   */
+  getList = (
+    query?: {
+      key?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListApiListOutput, any>({
+      path: `/api/admin/api/get-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name GetPage
+   * @summary 查询分页
+   * @request POST:/api/admin/api/get-page
+   * @secure
+   */
+  getPage = (data: PageInputApiGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputApiEntity, any>({
+      path: `/api/admin/api/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name Add
+   * @summary 添加
+   * @request POST:/api/admin/api/add
+   * @secure
+   */
+  add = (data: ApiAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/api/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/api/update
+   * @secure
+   */
+  update = (data: ApiUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/api/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/api/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/api/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/api/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/api/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name BatchSoftDelete
+   * @summary 批量删除
+   * @request PUT:/api/admin/api/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/api/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags api
+   * @name Sync
+   * @summary 同步
+   * @request POST:/api/admin/api/sync
+   * @secure
+   */
+  sync = (data: ApiSyncInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/api/sync`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 146 - 0
src/api/admin/Auth.ts

@@ -0,0 +1,146 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  AuthLoginInput,
+  ResultOutputAuthGetPasswordEncryptKeyOutput,
+  ResultOutputAuthGetUserInfoOutput,
+  ResultOutputCaptchaOutput,
+  ResultOutputObject,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Auth<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags auth
+   * @name GetPasswordEncryptKey
+   * @summary 查询密钥
+   * @request GET:/api/admin/auth/get-password-encrypt-key
+   * @secure
+   */
+  getPasswordEncryptKey = (params: RequestParams = {}) =>
+    this.request<ResultOutputAuthGetPasswordEncryptKeyOutput, any>({
+      path: `/api/admin/auth/get-password-encrypt-key`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags auth
+   * @name GetUserInfo
+   * @summary 查询用户信息
+   * @request GET:/api/admin/auth/get-user-info
+   * @secure
+   */
+  getUserInfo = (params: RequestParams = {}) =>
+    this.request<ResultOutputAuthGetUserInfoOutput, any>({
+      path: `/api/admin/auth/get-user-info`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags auth
+   * @name Login
+   * @summary 登录
+   * @request POST:/api/admin/auth/login
+   * @secure
+   */
+  login = (data: AuthLoginInput, params: RequestParams = {}) =>
+    this.request<ResultOutputObject, any>({
+      path: `/api/admin/auth/login`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+ * No description
+ *
+ * @tags auth
+ * @name Refresh
+ * @summary 刷新Token
+以旧换新
+ * @request GET:/api/admin/auth/refresh
+ * @secure
+ */
+  refresh = (
+    query: {
+      token: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputObject, any>({
+      path: `/api/admin/auth/refresh`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags auth
+   * @name GetCaptcha
+   * @summary 获取验证数据
+   * @request GET:/api/admin/auth/get-captcha
+   * @secure
+   */
+  getCaptcha = (params: RequestParams = {}) =>
+    this.request<ResultOutputCaptchaOutput, any>({
+      path: `/api/admin/auth/get-captcha`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags auth
+   * @name CheckCaptcha
+   * @summary 检查验证数据
+   * @request GET:/api/admin/auth/check-captcha
+   * @secure
+   */
+  checkCaptcha = (
+    query?: {
+      /** 校验唯一标识 */
+      Token?: string
+      /** 缓存键 */
+      CaptchaKey?: string
+      /** 删除缓存 */
+      DeleteCache?: boolean
+      /** 数据 */
+      Data?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/auth/check-captcha`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      ...params,
+    })
+}

+ 57 - 0
src/api/admin/Cache.ts

@@ -0,0 +1,57 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import { ResultOutputListObject } from './data-contracts'
+import { HttpClient, RequestParams } from './http-client'
+
+export class Cache<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags cache
+   * @name GetList
+   * @summary 查询列表
+   * @request GET:/api/admin/cache/get-list
+   * @secure
+   */
+  getList = (params: RequestParams = {}) =>
+    this.request<ResultOutputListObject, any>({
+      path: `/api/admin/cache/get-list`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags cache
+   * @name Clear
+   * @summary 清除缓存
+   * @request DELETE:/api/admin/cache/clear
+   * @secure
+   */
+  clear = (
+    query?: {
+      /** 缓存键 */
+      cacheKey?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/cache/clear`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+}

+ 168 - 0
src/api/admin/Dictionary.ts

@@ -0,0 +1,168 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  DictionaryAddInput,
+  DictionaryUpdateInput,
+  PageInputDictionaryGetPageDto,
+  ResultOutputDictionaryGetOutput,
+  ResultOutputInt64,
+  ResultOutputPageOutputDictionaryListOutput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Dictionary<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name Get
+   * @summary 查询数据字典
+   * @request GET:/api/admin/dictionary/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputDictionaryGetOutput, any>({
+      path: `/api/admin/dictionary/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name GetPage
+   * @summary 查询数据字典列表
+   * @request POST:/api/admin/dictionary/get-page
+   * @secure
+   */
+  getPage = (data: PageInputDictionaryGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputDictionaryListOutput, any>({
+      path: `/api/admin/dictionary/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/dictionary/add
+   * @secure
+   */
+  add = (data: DictionaryAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/dictionary/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/dictionary/update
+   * @secure
+   */
+  update = (data: DictionaryUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/dictionary/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/dictionary/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary
+   * @name BatchSoftDelete
+   * @summary 批量删除
+   * @request PUT:/api/admin/dictionary/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 168 - 0
src/api/admin/DictionaryType.ts

@@ -0,0 +1,168 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  DictionaryTypeAddInput,
+  DictionaryTypeUpdateInput,
+  PageInputDictionaryTypeGetPageDto,
+  ResultOutputDictionaryTypeGetOutput,
+  ResultOutputInt64,
+  ResultOutputPageOutputDictionaryTypeListOutput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class DictionaryType<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name Get
+   * @summary 查询字典类型
+   * @request GET:/api/admin/dictionary-type/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputDictionaryTypeGetOutput, any>({
+      path: `/api/admin/dictionary-type/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name GetPage
+   * @summary 查询字典类型列表
+   * @request POST:/api/admin/dictionary-type/get-page
+   * @secure
+   */
+  getPage = (data: PageInputDictionaryTypeGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputDictionaryTypeListOutput, any>({
+      path: `/api/admin/dictionary-type/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/dictionary-type/add
+   * @secure
+   */
+  add = (data: DictionaryTypeAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/dictionary-type/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/dictionary-type/update
+   * @secure
+   */
+  update = (data: DictionaryTypeUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary-type/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/dictionary-type/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary-type/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/dictionary-type/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary-type/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags dictionary-type
+   * @name BatchSoftDelete
+   * @summary 批量删除
+   * @request PUT:/api/admin/dictionary-type/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/dictionary-type/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 386 - 0
src/api/admin/Document.ts

@@ -0,0 +1,386 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  DocumentAddGroupInput,
+  DocumentAddImageInput,
+  DocumentAddMenuInput,
+  DocumentUpdateContentInput,
+  DocumentUpdateGroupInput,
+  DocumentUpdateMenuInput,
+  ResultOutputDocumentGetContentOutput,
+  ResultOutputDocumentGetGroupOutput,
+  ResultOutputDocumentGetMenuOutput,
+  ResultOutputIEnumerableObject,
+  ResultOutputInt64,
+  ResultOutputListDocumentListOutput,
+  ResultOutputListString,
+  ResultOutputString,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Document<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags document
+   * @name GetGroup
+   * @summary 查询分组
+   * @request GET:/api/admin/document/get-group
+   * @secure
+   */
+  getGroup = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputDocumentGetGroupOutput, any>({
+      path: `/api/admin/document/get-group`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name GetMenu
+   * @summary 查询菜单
+   * @request GET:/api/admin/document/get-menu
+   * @secure
+   */
+  getMenu = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputDocumentGetMenuOutput, any>({
+      path: `/api/admin/document/get-menu`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name GetContent
+   * @summary 查询文档内容
+   * @request GET:/api/admin/document/get-content
+   * @secure
+   */
+  getContent = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputDocumentGetContentOutput, any>({
+      path: `/api/admin/document/get-content`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name GetList
+   * @summary 查询文档列表
+   * @request GET:/api/admin/document/get-list
+   * @secure
+   */
+  getList = (
+    query?: {
+      key?: string
+      /** @format date-time */
+      start?: string
+      /** @format date-time */
+      end?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListDocumentListOutput, any>({
+      path: `/api/admin/document/get-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name GetImageList
+   * @summary 查询图片列表
+   * @request GET:/api/admin/document/get-image-list
+   * @secure
+   */
+  getImageList = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListString, any>({
+      path: `/api/admin/document/get-image-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name AddGroup
+   * @summary 新增分组
+   * @request POST:/api/admin/document/add-group
+   * @secure
+   */
+  addGroup = (data: DocumentAddGroupInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/document/add-group`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name AddMenu
+   * @summary 新增菜单
+   * @request POST:/api/admin/document/add-menu
+   * @secure
+   */
+  addMenu = (data: DocumentAddMenuInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/document/add-menu`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name AddImage
+   * @summary 新增图片
+   * @request POST:/api/admin/document/add-image
+   * @secure
+   */
+  addImage = (data: DocumentAddImageInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/document/add-image`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name UpdateGroup
+   * @summary 修改分组
+   * @request PUT:/api/admin/document/update-group
+   * @secure
+   */
+  updateGroup = (data: DocumentUpdateGroupInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/document/update-group`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name UpdateMenu
+   * @summary 修改菜单
+   * @request PUT:/api/admin/document/update-menu
+   * @secure
+   */
+  updateMenu = (data: DocumentUpdateMenuInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/document/update-menu`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name UpdateContent
+   * @summary 修改文档内容
+   * @request PUT:/api/admin/document/update-content
+   * @secure
+   */
+  updateContent = (data: DocumentUpdateContentInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/document/update-content`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name Delete
+   * @summary 彻底删除文档
+   * @request DELETE:/api/admin/document/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/document/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name DeleteImage
+   * @summary 彻底删除图片
+   * @request DELETE:/api/admin/document/delete-image
+   * @secure
+   */
+  deleteImage = (
+    query?: {
+      /** @format int64 */
+      documentId?: number
+      url?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/document/delete-image`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name SoftDelete
+   * @summary 删除文档
+   * @request DELETE:/api/admin/document/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/document/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name GetPlainList
+   * @summary 查询精简文档列表
+   * @request GET:/api/admin/document/get-plain-list
+   * @secure
+   */
+  getPlainList = (params: RequestParams = {}) =>
+    this.request<ResultOutputIEnumerableObject, any>({
+      path: `/api/admin/document/get-plain-list`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags document
+   * @name UploadImage
+   * @summary 上传文档图片
+   * @request POST:/api/admin/document/upload-image
+   * @secure
+   */
+  uploadImage = (
+    data: {
+      /**
+       * 上传文件
+       * @format binary
+       */
+      File?: File
+      /**
+       * 文档编号
+       * @format int64
+       */
+      Id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputString, any>({
+      path: `/api/admin/document/upload-image`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.FormData,
+      format: 'json',
+      ...params,
+    })
+}

+ 54 - 0
src/api/admin/LoginLog.ts

@@ -0,0 +1,54 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { LoginLogAddInput, PageInputLogGetPageDto, ResultOutputInt64, ResultOutputPageOutputLoginLogListOutput } from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class LoginLog<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags login-log
+   * @name GetPage
+   * @summary 查询登录日志列表
+   * @request POST:/api/admin/login-log/get-page
+   * @secure
+   */
+  getPage = (data: PageInputLogGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputLoginLogListOutput, any>({
+      path: `/api/admin/login-log/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags login-log
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/login-log/add
+   * @secure
+   */
+  add = (data: LoginLogAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/login-log/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+}

+ 54 - 0
src/api/admin/OprationLog.ts

@@ -0,0 +1,54 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { OprationLogAddInput, PageInputLogGetPageDto, ResultOutputInt64, ResultOutputPageOutputOprationLogListOutput } from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class OprationLog<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags opration-log
+   * @name GetPage
+   * @summary 查询操作日志列表
+   * @request POST:/api/admin/opration-log/get-page
+   * @secure
+   */
+  getPage = (data: PageInputLogGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputOprationLogListOutput, any>({
+      path: `/api/admin/opration-log/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags opration-log
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/opration-log/add
+   * @secure
+   */
+  add = (data: OprationLogAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/opration-log/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+}

+ 147 - 0
src/api/admin/Org.ts

@@ -0,0 +1,147 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import { OrgAddInput, OrgUpdateInput, ResultOutputInt64, ResultOutputListOrgListOutput, ResultOutputOrgGetOutput } from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Org<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags org
+   * @name Get
+   * @summary 查询部门
+   * @request GET:/api/admin/org/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputOrgGetOutput, any>({
+      path: `/api/admin/org/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags org
+   * @name GetList
+   * @summary 查询列表
+   * @request GET:/api/admin/org/get-list
+   * @secure
+   */
+  getList = (
+    query?: {
+      key?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListOrgListOutput, any>({
+      path: `/api/admin/org/get-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags org
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/org/add
+   * @secure
+   */
+  add = (data: OrgAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/org/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags org
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/org/update
+   * @secure
+   */
+  update = (data: OrgUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/org/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags org
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/org/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/org/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags org
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/org/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/org/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+}

+ 457 - 0
src/api/admin/Permission.ts

@@ -0,0 +1,457 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  PermissionAddApiInput,
+  PermissionAddDotInput,
+  PermissionAddGroupInput,
+  PermissionAddMenuInput,
+  PermissionAssignInput,
+  PermissionSaveTenantPermissionsInput,
+  PermissionUpdateApiInput,
+  PermissionUpdateDotInput,
+  PermissionUpdateGroupInput,
+  PermissionUpdateMenuInput,
+  ResultOutputIEnumerableObject,
+  ResultOutputInt64,
+  ResultOutputListInt64,
+  ResultOutputListPermissionListOutput,
+  ResultOutputPermissionGetApiOutput,
+  ResultOutputPermissionGetDotOutput,
+  ResultOutputPermissionGetGroupOutput,
+  ResultOutputPermissionGetMenuOutput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Permission<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetGroup
+   * @summary 查询分组
+   * @request GET:/api/admin/permission/get-group
+   * @secure
+   */
+  getGroup = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputPermissionGetGroupOutput, any>({
+      path: `/api/admin/permission/get-group`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetMenu
+   * @summary 查询菜单
+   * @request GET:/api/admin/permission/get-menu
+   * @secure
+   */
+  getMenu = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputPermissionGetMenuOutput, any>({
+      path: `/api/admin/permission/get-menu`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetApi
+   * @summary 查询接口
+   * @request GET:/api/admin/permission/get-api
+   * @secure
+   */
+  getApi = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputPermissionGetApiOutput, any>({
+      path: `/api/admin/permission/get-api`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetDot
+   * @summary 查询权限点
+   * @request GET:/api/admin/permission/get-dot
+   * @secure
+   */
+  getDot = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputPermissionGetDotOutput, any>({
+      path: `/api/admin/permission/get-dot`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetList
+   * @summary 查询权限列表
+   * @request GET:/api/admin/permission/get-list
+   * @secure
+   */
+  getList = (
+    query?: {
+      key?: string
+      /** @format date-time */
+      start?: string
+      /** @format date-time */
+      end?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListPermissionListOutput, any>({
+      path: `/api/admin/permission/get-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetPermissionList
+   * @summary 查询角色权限-权限列表
+   * @request GET:/api/admin/permission/get-permission-list
+   * @secure
+   */
+  getPermissionList = (params: RequestParams = {}) =>
+    this.request<ResultOutputIEnumerableObject, any>({
+      path: `/api/admin/permission/get-permission-list`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetRolePermissionList
+   * @summary 查询角色权限列表
+   * @request GET:/api/admin/permission/get-role-permission-list
+   * @secure
+   */
+  getRolePermissionList = (
+    query?: {
+      /**
+       * @format int64
+       * @default 0
+       */
+      roleId?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListInt64, any>({
+      path: `/api/admin/permission/get-role-permission-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name GetTenantPermissionList
+   * @summary 查询租户权限列表
+   * @request GET:/api/admin/permission/get-tenant-permission-list
+   * @secure
+   */
+  getTenantPermissionList = (
+    query?: {
+      /** @format int64 */
+      tenantId?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListInt64, any>({
+      path: `/api/admin/permission/get-tenant-permission-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name AddGroup
+   * @summary 新增分组
+   * @request POST:/api/admin/permission/add-group
+   * @secure
+   */
+  addGroup = (data: PermissionAddGroupInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/permission/add-group`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name AddMenu
+   * @summary 新增菜单
+   * @request POST:/api/admin/permission/add-menu
+   * @secure
+   */
+  addMenu = (data: PermissionAddMenuInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/permission/add-menu`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name AddApi
+   * @summary 新增接口
+   * @request POST:/api/admin/permission/add-api
+   * @secure
+   */
+  addApi = (data: PermissionAddApiInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/permission/add-api`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name AddDot
+   * @summary 新增权限点
+   * @request POST:/api/admin/permission/add-dot
+   * @secure
+   */
+  addDot = (data: PermissionAddDotInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/permission/add-dot`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name UpdateGroup
+   * @summary 修改分组
+   * @request PUT:/api/admin/permission/update-group
+   * @secure
+   */
+  updateGroup = (data: PermissionUpdateGroupInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/update-group`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name UpdateMenu
+   * @summary 修改菜单
+   * @request PUT:/api/admin/permission/update-menu
+   * @secure
+   */
+  updateMenu = (data: PermissionUpdateMenuInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/update-menu`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name UpdateApi
+   * @summary 修改接口
+   * @request PUT:/api/admin/permission/update-api
+   * @secure
+   */
+  updateApi = (data: PermissionUpdateApiInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/update-api`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name UpdateDot
+   * @summary 修改权限点
+   * @request PUT:/api/admin/permission/update-dot
+   * @secure
+   */
+  updateDot = (data: PermissionUpdateDotInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/update-dot`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/permission/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/permission/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name Assign
+   * @summary 保存角色权限
+   * @request POST:/api/admin/permission/assign
+   * @secure
+   */
+  assign = (data: PermissionAssignInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/assign`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags permission
+   * @name SaveTenantPermissions
+   * @summary 保存租户权限
+   * @request POST:/api/admin/permission/save-tenant-permissions
+   * @secure
+   */
+  saveTenantPermissions = (data: PermissionSaveTenantPermissionsInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/permission/save-tenant-permissions`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 297 - 0
src/api/admin/Role.ts

@@ -0,0 +1,297 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  PageInputRoleGetPageDto,
+  ResultOutputInt64,
+  ResultOutputListRoleGetListOutput,
+  ResultOutputListUserGetRoleUserListOutput,
+  ResultOutputPageOutputRoleGetPageOutput,
+  ResultOutputRoleGetOutput,
+  RoleAddInput,
+  RoleAddRoleUserListInput,
+  RoleSetDataScopeInput,
+  RoleUpdateInput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Role<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags role
+   * @name Get
+   * @summary 查询角色
+   * @request GET:/api/admin/role/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputRoleGetOutput, any>({
+      path: `/api/admin/role/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name GetList
+   * @summary 查询角色列表
+   * @request GET:/api/admin/role/get-list
+   * @secure
+   */
+  getList = (
+    query?: {
+      /** 名称 */
+      Name?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListRoleGetListOutput, any>({
+      path: `/api/admin/role/get-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name GetPage
+   * @summary 查询角色列表
+   * @request POST:/api/admin/role/get-page
+   * @secure
+   */
+  getPage = (data: PageInputRoleGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputRoleGetPageOutput, any>({
+      path: `/api/admin/role/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name GetRoleUserList
+   * @summary 查询角色用户列表
+   * @request GET:/api/admin/role/get-role-user-list
+   * @secure
+   */
+  getRoleUserList = (
+    query?: {
+      /** 姓名 */
+      Name?: string
+      /**
+       * 角色Id
+       * @format int64
+       */
+      RoleId?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListUserGetRoleUserListOutput, any>({
+      path: `/api/admin/role/get-role-user-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name AddRoleUser
+   * @summary 新增角色用户
+   * @request POST:/api/admin/role/add-role-user
+   * @secure
+   */
+  addRoleUser = (data: RoleAddRoleUserListInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/add-role-user`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name RemoveRoleUser
+   * @summary 移除角色用户
+   * @request POST:/api/admin/role/remove-role-user
+   * @secure
+   */
+  removeRoleUser = (data: RoleAddRoleUserListInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/remove-role-user`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name Add
+   * @summary 添加
+   * @request POST:/api/admin/role/add
+   * @secure
+   */
+  add = (data: RoleAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/role/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/role/update
+   * @secure
+   */
+  update = (data: RoleUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/role/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name BatchDelete
+   * @summary 批量彻底删除
+   * @request PUT:/api/admin/role/batch-delete
+   * @secure
+   */
+  batchDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/batch-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/role/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name BatchSoftDelete
+   * @summary 批量删除
+   * @request PUT:/api/admin/role/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags role
+   * @name SetDataScope
+   * @summary 设置数据权限
+   * @request POST:/api/admin/role/set-data-scope
+   * @secure
+   */
+  setDataScope = (data: RoleSetDataScopeInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/role/set-data-scope`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 192 - 0
src/api/admin/Task.ts

@@ -0,0 +1,192 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  PageInputTaskGetPageDto,
+  ResultOutputPageOutputTaskListOutput,
+  ResultOutputString,
+  ResultOutputTaskGetOutput,
+  TaskAddInput,
+  TaskUpdateInput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Task<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Get
+   * @summary 查询任务
+   * @request GET:/api/admin/task/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputTaskGetOutput, any>({
+      path: `/api/admin/task/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name GetPage
+   * @summary 查询任务列表
+   * @request POST:/api/admin/task/get-page
+   * @secure
+   */
+  getPage = (data: PageInputTaskGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputTaskListOutput, any>({
+      path: `/api/admin/task/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/task/add
+   * @secure
+   */
+  add = (data: TaskAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputString, any>({
+      path: `/api/admin/task/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/task/update
+   * @secure
+   */
+  update = (data: TaskUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/task/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Pause
+   * @summary 暂停任务
+   * @request POST:/api/admin/task/pause
+   * @secure
+   */
+  pause = (
+    query: {
+      id: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/task/pause`,
+      method: 'POST',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Resume
+   * @summary 启动任务
+   * @request POST:/api/admin/task/resume
+   * @secure
+   */
+  resume = (
+    query: {
+      id: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/task/resume`,
+      method: 'POST',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Run
+   * @summary 执行任务
+   * @request POST:/api/admin/task/run
+   * @secure
+   */
+  run = (
+    query: {
+      id: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/task/run`,
+      method: 'POST',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags task
+   * @name Delete
+   * @summary 删除任务
+   * @request DELETE:/api/admin/task/delete
+   * @secure
+   */
+  delete = (
+    query: {
+      id: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/task/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+}

+ 35 - 0
src/api/admin/TaskLog.ts

@@ -0,0 +1,35 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { PageInputTaskLogGetPageDto, ResultOutputPageOutputTaskLog } from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class TaskLog<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags task-log
+   * @name GetPage
+   * @summary 查询任务日志列表
+   * @request POST:/api/admin/task-log/get-page
+   * @secure
+   */
+  getPage = (data: PageInputTaskLogGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputTaskLog, any>({
+      path: `/api/admin/task-log/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+}

+ 168 - 0
src/api/admin/Tenant.ts

@@ -0,0 +1,168 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  PageInputTenantGetPageDto,
+  ResultOutputInt64,
+  ResultOutputPageOutputTenantListOutput,
+  ResultOutputTenantGetOutput,
+  TenantAddInput,
+  TenantUpdateInput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class Tenant<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name Get
+   * @summary 查询租户
+   * @request GET:/api/admin/tenant/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputTenantGetOutput, any>({
+      path: `/api/admin/tenant/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name GetPage
+   * @summary 查询租户列表
+   * @request POST:/api/admin/tenant/get-page
+   * @secure
+   */
+  getPage = (data: PageInputTenantGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputTenantListOutput, any>({
+      path: `/api/admin/tenant/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/tenant/add
+   * @secure
+   */
+  add = (data: TenantAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/tenant/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/tenant/update
+   * @secure
+   */
+  update = (data: TenantUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/tenant/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/tenant/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/tenant/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/tenant/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/tenant/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags tenant
+   * @name BatchSoftDelete
+   * @summary 批量删除
+   * @request PUT:/api/admin/tenant/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/tenant/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 369 - 0
src/api/admin/User.ts

@@ -0,0 +1,369 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  PageInputUserGetPageDto,
+  ResultOutputIListUserPermissionsOutput,
+  ResultOutputInt64,
+  ResultOutputPageOutputUserGetPageOutput,
+  ResultOutputString,
+  ResultOutputUserGetBasicOutput,
+  ResultOutputUserGetOutput,
+  UserAddInput,
+  UserAddMemberInput,
+  UserChangePasswordInput,
+  UserResetPasswordInput,
+  UserSetManagerInput,
+  UserUpdateBasicInput,
+  UserUpdateInput,
+  UserUpdateMemberInput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class User<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags user
+   * @name Get
+   * @summary 查询用户
+   * @request GET:/api/admin/user/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputUserGetOutput, any>({
+      path: `/api/admin/user/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name GetPage
+   * @summary 查询分页
+   * @request POST:/api/admin/user/get-page
+   * @secure
+   */
+  getPage = (data: PageInputUserGetPageDto, params: RequestParams = {}) =>
+    this.request<ResultOutputPageOutputUserGetPageOutput, any>({
+      path: `/api/admin/user/get-page`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name GetBasic
+   * @summary 查询用户基本信息
+   * @request GET:/api/admin/user/get-basic
+   * @secure
+   */
+  getBasic = (params: RequestParams = {}) =>
+    this.request<ResultOutputUserGetBasicOutput, any>({
+      path: `/api/admin/user/get-basic`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name GetPermissions
+   * @summary 查询用户权限信息
+   * @request GET:/api/admin/user/get-permissions
+   * @secure
+   */
+  getPermissions = (params: RequestParams = {}) =>
+    this.request<ResultOutputIListUserPermissionsOutput, any>({
+      path: `/api/admin/user/get-permissions`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name Add
+   * @summary 新增用户
+   * @request POST:/api/admin/user/add
+   * @secure
+   */
+  add = (data: UserAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/user/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name AddMember
+   * @summary 新增会员
+   * @request POST:/api/admin/user/add-member
+   * @secure
+   */
+  addMember = (data: UserAddMemberInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/user/add-member`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name UpdateMember
+   * @summary 修改会员
+   * @request PUT:/api/admin/user/update-member
+   * @secure
+   */
+  updateMember = (data: UserUpdateMemberInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/update-member`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name Update
+   * @summary 修改用户
+   * @request PUT:/api/admin/user/update
+   * @secure
+   */
+  update = (data: UserUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name UpdateBasic
+   * @summary 更新用户基本信息
+   * @request PUT:/api/admin/user/update-basic
+   * @secure
+   */
+  updateBasic = (data: UserUpdateBasicInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/update-basic`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name ChangePassword
+   * @summary 修改用户密码
+   * @request PUT:/api/admin/user/change-password
+   * @secure
+   */
+  changePassword = (data: UserChangePasswordInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/change-password`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name ResetPassword
+   * @summary 重置密码
+   * @request POST:/api/admin/user/reset-password
+   * @secure
+   */
+  resetPassword = (data: UserResetPasswordInput, params: RequestParams = {}) =>
+    this.request<ResultOutputString, any>({
+      path: `/api/admin/user/reset-password`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name SetManager
+   * @summary 设置主管
+   * @request POST:/api/admin/user/set-manager
+   * @secure
+   */
+  setManager = (data: UserSetManagerInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/set-manager`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name Delete
+   * @summary 彻底删除用户
+   * @request DELETE:/api/admin/user/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name BatchDelete
+   * @summary 批量彻底删除用户
+   * @request PUT:/api/admin/user/batch-delete
+   * @secure
+   */
+  batchDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/batch-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name SoftDelete
+   * @summary 删除用户
+   * @request DELETE:/api/admin/user/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name BatchSoftDelete
+   * @summary 批量删除用户
+   * @request PUT:/api/admin/user/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/user/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags user
+   * @name AvatarUpload
+   * @summary 上传头像
+   * @request POST:/api/admin/user/avatar-upload
+   * @secure
+   */
+  avatarUpload = (
+    data: {
+      ContentType?: string
+      ContentDisposition?: string
+      Headers?: Record<string, string[]>
+      /** @format int64 */
+      Length?: number
+      Name?: string
+      FileName?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputString, any>({
+      path: `/api/admin/user/avatar-upload`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.FormData,
+      format: 'json',
+      ...params,
+    })
+}

+ 190 - 0
src/api/admin/View.ts

@@ -0,0 +1,190 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import { AxiosResponse } from 'axios'
+import {
+  ResultOutputInt64,
+  ResultOutputListViewListOutput,
+  ResultOutputViewGetOutput,
+  ViewAddInput,
+  ViewSyncInput,
+  ViewUpdateInput,
+} from './data-contracts'
+import { ContentType, HttpClient, RequestParams } from './http-client'
+
+export class View<SecurityDataType = unknown> extends HttpClient<SecurityDataType> {
+  /**
+   * No description
+   *
+   * @tags view
+   * @name Get
+   * @summary 查询视图
+   * @request GET:/api/admin/view/get
+   * @secure
+   */
+  get = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputViewGetOutput, any>({
+      path: `/api/admin/view/get`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name GetList
+   * @summary 查询列表
+   * @request GET:/api/admin/view/get-list
+   * @secure
+   */
+  getList = (
+    query?: {
+      key?: string
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<ResultOutputListViewListOutput, any>({
+      path: `/api/admin/view/get-list`,
+      method: 'GET',
+      query: query,
+      secure: true,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name Add
+   * @summary 新增
+   * @request POST:/api/admin/view/add
+   * @secure
+   */
+  add = (data: ViewAddInput, params: RequestParams = {}) =>
+    this.request<ResultOutputInt64, any>({
+      path: `/api/admin/view/add`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      format: 'json',
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name Update
+   * @summary 修改
+   * @request PUT:/api/admin/view/update
+   * @secure
+   */
+  update = (data: ViewUpdateInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/view/update`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name Delete
+   * @summary 彻底删除
+   * @request DELETE:/api/admin/view/delete
+   * @secure
+   */
+  delete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/view/delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name SoftDelete
+   * @summary 删除
+   * @request DELETE:/api/admin/view/soft-delete
+   * @secure
+   */
+  softDelete = (
+    query?: {
+      /** @format int64 */
+      id?: number
+    },
+    params: RequestParams = {}
+  ) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/view/soft-delete`,
+      method: 'DELETE',
+      query: query,
+      secure: true,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name BatchSoftDelete
+   * @summary 批量删除
+   * @request PUT:/api/admin/view/batch-soft-delete
+   * @secure
+   */
+  batchSoftDelete = (data: number[], params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/view/batch-soft-delete`,
+      method: 'PUT',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+  /**
+   * No description
+   *
+   * @tags view
+   * @name Sync
+   * @summary 同步
+   * @request POST:/api/admin/view/sync
+   * @secure
+   */
+  sync = (data: ViewSyncInput, params: RequestParams = {}) =>
+    this.request<AxiosResponse, any>({
+      path: `/api/admin/view/sync`,
+      method: 'POST',
+      body: data,
+      secure: true,
+      type: ContentType.Json,
+      ...params,
+    })
+}

+ 3707 - 0
src/api/admin/data-contracts.ts

@@ -0,0 +1,3707 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+/** 添加 */
+export interface ApiAddInput {
+  /**
+   * 所属模块
+   * @format int64
+   */
+  parentId?: number | null
+  /** 接口名称 */
+  label?: string | null
+  /** 接口地址 */
+  path?: string | null
+  /** 接口提交方法 */
+  httpMethods?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+/** 接口管理 */
+export interface ApiEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 所属模块
+   * @format int64
+   */
+  parentId?: number
+  /** 接口命名 */
+  name?: string | null
+  /** 接口名称 */
+  label?: string | null
+  /** 接口地址 */
+  path?: string | null
+  /** 接口提交方法 */
+  httpMethods?: string | null
+  /** 说明 */
+  description?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  childs?: ApiEntity[] | null
+  permissions?: PermissionEntity[] | null
+}
+
+export interface ApiGetOutput {
+  /**
+   * 所属模块
+   * @format int64
+   */
+  parentId?: number | null
+  /** 接口名称 */
+  label?: string | null
+  /** 接口地址 */
+  path?: string | null
+  /** 接口提交方法 */
+  httpMethods?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 接口Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface ApiGetPageDto {
+  /** 接口名称 */
+  label?: string | null
+}
+
+export interface ApiListOutput {
+  /**
+   * 接口Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 接口父级
+   * @format int64
+   */
+  parentId?: number | null
+  /** 接口命名 */
+  name?: string | null
+  /** 接口名称 */
+  label?: string | null
+  /** 接口地址 */
+  path?: string | null
+  /** 接口提交方法 */
+  httpMethods?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+/** 接口同步Dto */
+export interface ApiSyncDto {
+  /** 接口名称 */
+  label?: string | null
+  /** 接口地址 */
+  path?: string | null
+  /** 父级路径 */
+  parentPath?: string | null
+  /** 接口提交方法 */
+  httpMethods?: string | null
+}
+
+/** 接口同步 */
+export interface ApiSyncInput {
+  apis?: ApiSyncDto[] | null
+}
+
+/** 修改 */
+export interface ApiUpdateInput {
+  /**
+   * 所属模块
+   * @format int64
+   */
+  parentId?: number | null
+  /** 接口名称 */
+  label?: string | null
+  /** 接口地址 */
+  path?: string | null
+  /** 接口提交方法 */
+  httpMethods?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 接口Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface AuthGetPasswordEncryptKeyOutput {
+  /** 缓存键 */
+  key?: string | null
+  /** 密码加密密钥 */
+  encyptKey?: string | null
+}
+
+export interface AuthGetUserInfoOutput {
+  user?: AuthUserProfileDto
+  /** 用户菜单 */
+  menus?: AuthUserMenuDto[] | null
+  /** 用户权限点 */
+  permissions?: string[] | null
+}
+
+/** 登录信息 */
+export interface AuthLoginInput {
+  /**
+   * 账号
+   * @minLength 1
+   */
+  userName: string
+  /**
+   * 密码
+   * @minLength 1
+   */
+  password: string
+  /** 密码键 */
+  passwordKey?: string | null
+  captcha?: CaptchaInput
+}
+
+export interface AuthUserMenuDto {
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 路由地址 */
+  path?: string | null
+  /** 路由命名 */
+  name?: string | null
+  /** 视图地址 */
+  viewPath?: string | null
+  /** 重定向地址 */
+  redirect?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 图标 */
+  icon?: string | null
+  /** 打开 */
+  opened?: boolean | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 打开新窗口 */
+  newWindow?: boolean | null
+  /** 链接外显 */
+  external?: boolean | null
+  /** 是否缓存 */
+  isKeepAlive?: boolean
+  /** 是否固定 */
+  isAffix?: boolean
+  /** 链接地址 */
+  link?: string | null
+  /** 是否内嵌窗口 */
+  isIframe?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number | null
+}
+
+export interface AuthUserProfileDto {
+  /** 账号 */
+  userName?: string | null
+  /** 姓名 */
+  name?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+}
+
+export interface CaptchaInput {
+  /** 校验唯一标识 */
+  token?: string | null
+  /** 缓存键 */
+  captchaKey?: string | null
+  /** 删除缓存 */
+  deleteCache?: boolean
+  /** 数据 */
+  data?: string | null
+}
+
+/** 验证数据 */
+export interface CaptchaOutput {
+  /** 校验唯一标识 */
+  token?: string | null
+  /** 数据 */
+  data?: any
+}
+
+/**
+ * 数据范围:All=1,DeptWithChild=2,Dept=3,Self=4,Custom=5
+ * @format int32
+ */
+export type DataScope = 1 | 2 | 3 | 4 | 5
+
+/**
+ * MySql=0,SqlServer=1,PostgreSQL=2,Oracle=3,Sqlite=4,OdbcOracle=5,OdbcSqlServer=6,OdbcMySql=7,OdbcPostgreSQL=8,Odbc=9,OdbcDameng=10,MsAccess=11,Dameng=12,OdbcKingbaseES=13,ShenTong=14,KingbaseES=15,Firebird=16,Custom=17,ClickHouse=18,GBase=19,CustomOracle=20,CustomSqlServer=21,CustomMySql=22,CustomPostgreSQL=23
+ * @format int32
+ */
+export type DataType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23
+
+/** 添加 */
+export interface DictionaryAddInput {
+  /**
+   * 字典类型Id
+   * @format int64
+   */
+  dictionaryTypeId?: number
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 字典值 */
+  value?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface DictionaryGetOutput {
+  /**
+   * 字典类型Id
+   * @format int64
+   */
+  dictionaryTypeId?: number
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 字典值 */
+  value?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface DictionaryGetPageDto {
+  /**
+   * 字典类型Id
+   * @format int64
+   */
+  dictionaryTypeId?: number
+  /** 字典名称 */
+  name?: string | null
+}
+
+export interface DictionaryListOutput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 字典值 */
+  value?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+/** 添加 */
+export interface DictionaryTypeAddInput {
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface DictionaryTypeGetOutput {
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface DictionaryTypeGetPageDto {
+  /** 字典名称 */
+  name?: string | null
+}
+
+export interface DictionaryTypeListOutput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+/** 修改 */
+export interface DictionaryTypeUpdateInput {
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+/** 修改 */
+export interface DictionaryUpdateInput {
+  /**
+   * 字典类型Id
+   * @format int64
+   */
+  dictionaryTypeId?: number
+  /** 字典名称 */
+  name?: string | null
+  /** 字典编码 */
+  code?: string | null
+  /** 字典值 */
+  value?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface DocumentAddGroupInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 名称 */
+  label?: string | null
+  /** 命名 */
+  name?: string | null
+  /** 打开 */
+  opened?: boolean | null
+}
+
+export interface DocumentAddImageInput {
+  /**
+   * 用户Id
+   * @format int64
+   */
+  documentId?: number
+  /** 请求路径 */
+  url?: string | null
+}
+
+export interface DocumentAddMenuInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 命名 */
+  name?: string | null
+  /** 名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+}
+
+export interface DocumentGetContentOutput {
+  /**
+   * 编号
+   * @format int64
+   */
+  id?: number
+  /** 名称 */
+  label?: string | null
+  /** 内容 */
+  content?: string | null
+}
+
+export interface DocumentGetGroupOutput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 名称 */
+  label?: string | null
+  /** 命名 */
+  name?: string | null
+  /** 打开 */
+  opened?: boolean | null
+  /**
+   * 编号
+   * @format int64
+   */
+  id: number
+}
+
+export interface DocumentGetMenuOutput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 命名 */
+  name?: string | null
+  /** 名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+  /**
+   * 编号
+   * @format int64
+   */
+  id: number
+}
+
+export interface DocumentListOutput {
+  /**
+   * 编号
+   * @format int64
+   */
+  id?: number
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  label?: string | null
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 命名 */
+  name?: string | null
+  /** 描述 */
+  description?: string | null
+  /** 组打开 */
+  opened?: boolean | null
+}
+
+/**
+ * 文档类型:Group=1,Markdown=2
+ * @format int32
+ */
+export type DocumentType = 1 | 2
+
+export interface DocumentUpdateContentInput {
+  /**
+   * 编号
+   * @format int64
+   */
+  id: number
+  /** 名称 */
+  label?: string | null
+  /** 内容 */
+  content?: string | null
+  /** Html */
+  html?: string | null
+}
+
+export interface DocumentUpdateGroupInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 名称 */
+  label?: string | null
+  /** 命名 */
+  name?: string | null
+  /** 打开 */
+  opened?: boolean | null
+  /**
+   * 编号
+   * @format int64
+   */
+  id: number
+}
+
+export interface DocumentUpdateMenuInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 文档类型:Group=1,Markdown=2 */
+  type?: DocumentType
+  /** 命名 */
+  name?: string | null
+  /** 名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+  /**
+   * 编号
+   * @format int64
+   */
+  id: number
+}
+
+export interface DynamicFilterInfo {
+  field?: string | null
+  /** Contains=0,StartsWith=1,EndsWith=2,NotContains=3,NotStartsWith=4,NotEndsWith=5,Equal=6,Equals=7,Eq=8,NotEqual=9,GreaterThan=10,GreaterThanOrEqual=11,LessThan=12,LessThanOrEqual=13,Range=14,DateRange=15,Any=16,NotAny=17,Custom=18 */
+  operator?: DynamicFilterOperator
+  value?: any
+  /** And=0,Or=1 */
+  logic?: DynamicFilterLogic
+  filters?: DynamicFilterInfo[] | null
+}
+
+/**
+ * And=0,Or=1
+ * @format int32
+ */
+export type DynamicFilterLogic = 0 | 1
+
+/**
+ * Contains=0,StartsWith=1,EndsWith=2,NotContains=3,NotStartsWith=4,NotEndsWith=5,Equal=6,Equals=7,Eq=8,NotEqual=9,GreaterThan=10,GreaterThanOrEqual=11,LessThan=12,LessThanOrEqual=13,Range=14,DateRange=15,Any=16,NotAny=17,Custom=18
+ * @format int32
+ */
+export type DynamicFilterOperator = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18
+
+export interface LogGetPageDto {
+  /** 创建者 */
+  createdUserName?: string | null
+}
+
+/** 添加 */
+export interface LoginLogAddInput {
+  /**
+   * 租户Id
+   * @format int64
+   */
+  tenantId?: number | null
+  /** 姓名 */
+  name?: string | null
+  /** IP */
+  ip?: string | null
+  /** 浏览器 */
+  browser?: string | null
+  /** 操作系统 */
+  os?: string | null
+  /** 设备 */
+  device?: string | null
+  /** 浏览器信息 */
+  browserInfo?: string | null
+  /**
+   * 耗时(毫秒)
+   * @format int64
+   */
+  elapsedMilliseconds?: number
+  /** 操作状态 */
+  status?: boolean | null
+  /** 操作消息 */
+  msg?: string | null
+  /** 操作结果 */
+  result?: string | null
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /** 创建者 */
+  createdUserName?: string | null
+}
+
+export interface LoginLogListOutput {
+  /**
+   * 编号
+   * @format int64
+   */
+  id?: number
+  /** 昵称 */
+  nickName?: string | null
+  /** 创建者 */
+  createdUserName?: string | null
+  /** IP */
+  ip?: string | null
+  /** 浏览器 */
+  browser?: string | null
+  /** 操作系统 */
+  os?: string | null
+  /** 设备 */
+  device?: string | null
+  /**
+   * 耗时(毫秒)
+   * @format int64
+   */
+  elapsedMilliseconds?: number
+  /** 操作状态 */
+  status?: boolean
+  /** 操作消息 */
+  msg?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+}
+
+/** 添加 */
+export interface OprationLogAddInput {
+  /** 姓名 */
+  name?: string | null
+  /** 接口名称 */
+  apiLabel?: string | null
+  /** 接口地址 */
+  apiPath?: string | null
+  /** 接口提交方法 */
+  apiMethod?: string | null
+  /** IP */
+  ip?: string | null
+  /** 浏览器 */
+  browser?: string | null
+  /** 操作系统 */
+  os?: string | null
+  /** 设备 */
+  device?: string | null
+  /** 浏览器信息 */
+  browserInfo?: string | null
+  /**
+   * 耗时(毫秒)
+   * @format int64
+   */
+  elapsedMilliseconds?: number
+  /** 操作状态 */
+  status?: boolean | null
+  /** 操作消息 */
+  msg?: string | null
+  /** 操作参数 */
+  params?: string | null
+  /** 操作结果 */
+  result?: string | null
+}
+
+export interface OprationLogListOutput {
+  /**
+   * 编号
+   * @format int64
+   */
+  id?: number
+  /** 昵称 */
+  nickName?: string | null
+  /** 创建者 */
+  createdUserName?: string | null
+  /** 接口名称 */
+  apiLabel?: string | null
+  /** 接口地址 */
+  apiPath?: string | null
+  /** 接口提交方法 */
+  apiMethod?: string | null
+  /** IP */
+  ip?: string | null
+  /** 浏览器 */
+  browser?: string | null
+  /** 操作系统 */
+  os?: string | null
+  /** 设备 */
+  device?: string | null
+  /**
+   * 耗时(毫秒)
+   * @format int64
+   */
+  elapsedMilliseconds?: number
+  /** 操作状态 */
+  status?: boolean
+  /** 操作消息 */
+  msg?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+}
+
+/** 添加 */
+export interface OrgAddInput {
+  /**
+   * 父级
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 值 */
+  value?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+}
+
+/** 组织架构 */
+export interface OrgEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 租户Id
+   * @format int64
+   */
+  tenantId?: number | null
+  /**
+   * 父级
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 值 */
+  value?: string | null
+  /**
+   * 成员数
+   * @format int32
+   */
+  memberCount?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+  /** 员工列表 */
+  staffs?: UserStaffEntity[] | null
+  /** 用户列表 */
+  users?: UserEntity[] | null
+  /** 角色列表 */
+  roles?: RoleEntity[] | null
+  /** 子级列表 */
+  childs?: OrgEntity[] | null
+}
+
+export interface OrgGetOutput {
+  /**
+   * 父级
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 值 */
+  value?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface OrgListOutput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 父级
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 值 */
+  value?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+}
+
+/** 修改 */
+export interface OrgUpdateInput {
+  /**
+   * 父级
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 值 */
+  value?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+/** 分页信息输入 */
+export interface PageInputApiGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: ApiGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputDictionaryGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: DictionaryGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputDictionaryTypeGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: DictionaryTypeGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputLogGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: LogGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputRoleGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: RoleGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputTaskGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: TaskGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputTaskLogGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: TaskLogGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputTenantGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  filter?: TenantGetPageDto
+}
+
+/** 分页信息输入 */
+export interface PageInputUserGetPageDto {
+  /**
+   * 当前页标
+   * @format int32
+   */
+  currentPage?: number
+  /**
+   * 每页大小
+   * @format int32
+   */
+  pageSize?: number
+  dynamicFilter?: DynamicFilterInfo
+  /** 用户分页查询条件 */
+  filter?: UserGetPageDto
+}
+
+/** 分页信息输出 */
+export interface PageOutputApiEntity {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: ApiEntity[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputDictionaryListOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: DictionaryListOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputDictionaryTypeListOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: DictionaryTypeListOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputLoginLogListOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: LoginLogListOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputOprationLogListOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: OprationLogListOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputRoleGetPageOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: RoleGetPageOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputTaskListOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: TaskListOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputTaskLog {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: TaskLog[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputTenantListOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: TenantListOutput[] | null
+}
+
+/** 分页信息输出 */
+export interface PageOutputUserGetPageOutput {
+  /**
+   * 数据总数
+   * @format int64
+   */
+  total?: number
+  /** 数据 */
+  list?: UserGetPageOutput[] | null
+}
+
+export interface PermissionAddApiInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 接口
+   * @format int64
+   */
+  apiId?: number | null
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface PermissionAddDotInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 关联接口 */
+  apiIds?: number[] | null
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface PermissionAddGroupInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 视图
+   * @format int64
+   */
+  viewId?: number | null
+  /** 路由命名 */
+  name?: string | null
+  /** 访问路由地址 */
+  path?: string | null
+  /** 重定向地址 */
+  redirect?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /** 展开 */
+  opened?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface PermissionAddMenuInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 视图
+   * @format int64
+   */
+  viewId?: number | null
+  /** 路由命名 */
+  name?: string | null
+  /** 路由地址 */
+  path?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /** 打开新窗口 */
+  newWindow?: boolean
+  /** 链接外显 */
+  external?: boolean
+  /** 是否缓存 */
+  isKeepAlive?: boolean
+  /** 是否固定 */
+  isAffix?: boolean
+  /** 链接地址 */
+  link?: string | null
+  /** 是否内嵌窗口 */
+  isIframe?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface PermissionAssignInput {
+  /** @format int64 */
+  roleId: number
+  permissionIds: number[]
+}
+
+/** 权限 */
+export interface PermissionEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 权限类型:Group=1,Menu=2,Dot=3 */
+  type?: PermissionType
+  /**
+   * 视图Id
+   * @format int64
+   */
+  viewId?: number | null
+  /** 视图管理 */
+  view?: ViewEntity
+  /** 路由命名 */
+  name?: string | null
+  /** 路由地址 */
+  path?: string | null
+  /** 重定向地址 */
+  redirect?: string | null
+  /** 图标 */
+  icon?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 展开分组 */
+  opened?: boolean
+  /** 打开新窗口 */
+  newWindow?: boolean
+  /** 链接外显 */
+  external?: boolean
+  /** 是否缓存 */
+  isKeepAlive?: boolean
+  /** 是否固定 */
+  isAffix?: boolean
+  /** 链接地址 */
+  link?: string | null
+  /** 是否内嵌窗口 */
+  isIframe?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+  apis?: ApiEntity[] | null
+  childs?: PermissionEntity[] | null
+}
+
+export interface PermissionGetApiOutput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 接口
+   * @format int64
+   */
+  apiId?: number | null
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionGetDotOutput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 关联接口 */
+  apiIds?: number[] | null
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionGetGroupOutput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 视图
+   * @format int64
+   */
+  viewId?: number | null
+  /** 路由命名 */
+  name?: string | null
+  /** 访问路由地址 */
+  path?: string | null
+  /** 重定向地址 */
+  redirect?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /** 展开 */
+  opened?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionGetMenuOutput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 视图
+   * @format int64
+   */
+  viewId?: number | null
+  /** 路由命名 */
+  name?: string | null
+  /** 路由地址 */
+  path?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /** 打开新窗口 */
+  newWindow?: boolean
+  /** 链接外显 */
+  external?: boolean
+  /** 是否缓存 */
+  isKeepAlive?: boolean
+  /** 是否固定 */
+  isAffix?: boolean
+  /** 链接地址 */
+  link?: string | null
+  /** 是否内嵌窗口 */
+  isIframe?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionListOutput {
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 权限名称 */
+  label?: string | null
+  /** 权限类型:Group=1,Menu=2,Dot=3 */
+  type?: PermissionType
+  /** 路由地址 */
+  path?: string | null
+  /** 重定向地址 */
+  redirect?: string | null
+  /** 视图地址 */
+  viewPath?: string | null
+  /** 链接地址 */
+  link?: string | null
+  /** 接口路径 */
+  apiPaths?: string | null
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number | null
+  /** 描述 */
+  description?: string | null
+  /** 启用 */
+  enabled?: boolean
+}
+
+export interface PermissionSaveTenantPermissionsInput {
+  /** @format int64 */
+  tenantId: number
+  permissionIds: number[]
+}
+
+/**
+ * 权限类型:Group=1,Menu=2,Dot=3
+ * @format int32
+ */
+export type PermissionType = 1 | 2 | 3
+
+export interface PermissionUpdateApiInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 接口
+   * @format int64
+   */
+  apiId?: number | null
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionUpdateDotInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /** 关联接口 */
+  apiIds?: number[] | null
+  /** 权限名称 */
+  label?: string | null
+  /** 权限编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 图标 */
+  icon?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionUpdateGroupInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 视图
+   * @format int64
+   */
+  viewId?: number | null
+  /** 路由命名 */
+  name?: string | null
+  /** 访问路由地址 */
+  path?: string | null
+  /** 重定向地址 */
+  redirect?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /** 展开 */
+  opened?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface PermissionUpdateMenuInput {
+  /**
+   * 父级节点
+   * @format int64
+   */
+  parentId?: number
+  /**
+   * 视图
+   * @format int64
+   */
+  viewId?: number | null
+  /** 路由命名 */
+  name?: string | null
+  /** 路由地址 */
+  path?: string | null
+  /** 权限名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /** 图标 */
+  icon?: string | null
+  /** 打开新窗口 */
+  newWindow?: boolean
+  /** 链接外显 */
+  external?: boolean
+  /** 是否缓存 */
+  isKeepAlive?: boolean
+  /** 是否固定 */
+  isAffix?: boolean
+  /** 链接地址 */
+  link?: string | null
+  /** 是否内嵌窗口 */
+  isIframe?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 权限Id
+   * @format int64
+   */
+  id: number
+}
+
+/** 结果输出 */
+export interface ResultOutputApiGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: ApiGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputAuthGetPasswordEncryptKeyOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: AuthGetPasswordEncryptKeyOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputAuthGetUserInfoOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: AuthGetUserInfoOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputCaptchaOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 验证数据 */
+  data?: CaptchaOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputDictionaryGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: DictionaryGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputDictionaryTypeGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: DictionaryTypeGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputDocumentGetContentOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: DocumentGetContentOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputDocumentGetGroupOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: DocumentGetGroupOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputDocumentGetMenuOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: DocumentGetMenuOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputIEnumerableObject {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: any[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputIListUserPermissionsOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: UserPermissionsOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputInt64 {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /**
+   * 数据
+   * @format int64
+   */
+  data?: number
+}
+
+/** 结果输出 */
+export interface ResultOutputListApiListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: ApiListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListDocumentListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: DocumentListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListInt64 {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: number[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListObject {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: any[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListOrgListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: OrgListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListPermissionListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: PermissionListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListRoleGetListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: RoleGetListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListString {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: string[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListUserGetRoleUserListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: UserGetRoleUserListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputListViewListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: ViewListOutput[] | null
+}
+
+/** 结果输出 */
+export interface ResultOutputObject {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: any
+}
+
+/** 结果输出 */
+export interface ResultOutputOrgGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: OrgGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputApiEntity {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputApiEntity
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputDictionaryListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputDictionaryListOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputDictionaryTypeListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputDictionaryTypeListOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputLoginLogListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputLoginLogListOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputOprationLogListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputOprationLogListOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputRoleGetPageOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputRoleGetPageOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputTaskListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputTaskListOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputTaskLog {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputTaskLog
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputTenantListOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputTenantListOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPageOutputUserGetPageOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 分页信息输出 */
+  data?: PageOutputUserGetPageOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPermissionGetApiOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: PermissionGetApiOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPermissionGetDotOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: PermissionGetDotOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPermissionGetGroupOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: PermissionGetGroupOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputPermissionGetMenuOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: PermissionGetMenuOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputRoleGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: RoleGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputString {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  /** 数据 */
+  data?: string | null
+}
+
+/** 结果输出 */
+export interface ResultOutputTaskGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: TaskGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputTenantGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: TenantGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputUserGetBasicOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: UserGetBasicOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputUserGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: UserGetOutput
+}
+
+/** 结果输出 */
+export interface ResultOutputViewGetOutput {
+  /** 是否成功标记 */
+  success?: boolean
+  /** 编码 */
+  code?: string | null
+  /** 消息 */
+  msg?: string | null
+  data?: ViewGetOutput
+}
+
+/** 添加 */
+export interface RoleAddInput {
+  /**
+   * 父级Id
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 角色类型:Group=1,Menu=2 */
+  type?: RoleType
+  /** 数据范围:All=1,DeptWithChild=2,Dept=3,Self=4,Custom=5 */
+  dataScope?: DataScope
+  /** 指定部门 */
+  orgIds?: number[] | null
+  /** 部门列表 */
+  orgs?: OrgEntity[] | null
+  /** 说明 */
+  description?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+}
+
+/** 添加角色用户列表 */
+export interface RoleAddRoleUserListInput {
+  /**
+   * 角色
+   * @format int64
+   */
+  roleId: number
+  /** 用户 */
+  userIds?: number[] | null
+}
+
+/** 角色 */
+export interface RoleEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 租户Id
+   * @format int64
+   */
+  tenantId?: number | null
+  /**
+   * 父级Id
+   * @format int64
+   */
+  parentId?: number
+  /** 子级列表 */
+  childs?: RoleEntity[] | null
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 角色类型:Group=1,Menu=2 */
+  type?: RoleType
+  /** 数据范围:All=1,DeptWithChild=2,Dept=3,Self=4,Custom=5 */
+  dataScope?: DataScope
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 用户列表 */
+  users?: UserEntity[] | null
+  /** 部门列表 */
+  orgs?: OrgEntity[] | null
+  /** 权限列表 */
+  permissions?: PermissionEntity[] | null
+}
+
+export interface RoleGetListOutput {
+  /**
+   * 主键
+   * @format int64
+   */
+  id?: number
+  /**
+   * 父级Id
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 角色类型:Group=1,Menu=2 */
+  type?: RoleType
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 描述 */
+  description?: string | null
+}
+
+export interface RoleGetOutput {
+  /**
+   * 父级Id
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 角色类型:Group=1,Menu=2 */
+  type?: RoleType
+  /** 数据范围:All=1,DeptWithChild=2,Dept=3,Self=4,Custom=5 */
+  dataScope?: DataScope
+  /** 指定部门 */
+  orgIds?: number[] | null
+  /** 部门列表 */
+  orgs?: OrgEntity[] | null
+  /** 说明 */
+  description?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /**
+   * 角色Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface RoleGetPageDto {
+  /** 名称 */
+  name?: string | null
+}
+
+export interface RoleGetPageOutput {
+  /**
+   * 主键
+   * @format int64
+   */
+  id?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 隐藏 */
+  hidden?: boolean
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+}
+
+/** 设置数据范围 */
+export interface RoleSetDataScopeInput {
+  /**
+   * 角色Id
+   * @format int64
+   */
+  roleId: number
+  /** 数据范围:All=1,DeptWithChild=2,Dept=3,Self=4,Custom=5 */
+  dataScope?: DataScope
+  /** 指定部门 */
+  orgIds?: number[] | null
+}
+
+/**
+ * 角色类型:Group=1,Menu=2
+ * @format int32
+ */
+export type RoleType = 1 | 2
+
+/** 修改 */
+export interface RoleUpdateInput {
+  /**
+   * 父级Id
+   * @format int64
+   */
+  parentId?: number
+  /** 名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 角色类型:Group=1,Menu=2 */
+  type?: RoleType
+  /** 数据范围:All=1,DeptWithChild=2,Dept=3,Self=4,Custom=5 */
+  dataScope?: DataScope
+  /** 指定部门 */
+  orgIds?: number[] | null
+  /** 部门列表 */
+  orgs?: OrgEntity[] | null
+  /** 说明 */
+  description?: string | null
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /**
+   * 角色Id
+   * @format int64
+   */
+  id: number
+}
+
+/**
+ * 性别:Unknown=0,Male=1,Female=2
+ * @format int32
+ */
+export type Sex = 0 | 1 | 2
+
+/** 员工添加 */
+export interface StaffAddInput {
+  /** 工号 */
+  jobNumber?: string | null
+  /** 职位 */
+  position?: string | null
+  /** 性别:Unknown=0,Male=1,Female=2 */
+  sex?: Sex
+  /**
+   * 入职时间
+   * @format date-time
+   */
+  entryTime?: string | null
+  /** 个人简介 */
+  introduce?: string | null
+}
+
+/** 添加 */
+export interface TaskAddInput {
+  /** 任务标题 */
+  topic?: string | null
+  /** 任务数据 */
+  body?: string | null
+  /**
+   * 任务执行多少轮,-1为永久循环
+   * @format int32
+   */
+  round?: number
+  /** SEC=1,RunOnDay=11,RunOnWeek=12,RunOnMonth=13,Custom=21 */
+  interval?: TaskInterval
+  /** 定时参数值 60,60,60,120,120,1200,1200 */
+  intervalArgument?: string | null
+}
+
+export interface TaskGetOutput {
+  /** 任务标题 */
+  topic?: string | null
+  /** 任务数据 */
+  body?: string | null
+  /**
+   * 任务执行多少轮,-1为永久循环
+   * @format int32
+   */
+  round?: number
+  /** SEC=1,RunOnDay=11,RunOnWeek=12,RunOnMonth=13,Custom=21 */
+  interval?: TaskInterval
+  /** 定时参数值 60,60,60,120,120,1200,1200 */
+  intervalArgument?: string | null
+  /**
+   * 任务Id
+   * @minLength 1
+   */
+  id: string
+}
+
+export interface TaskGetPageDto {
+  /** 任务名称名称 */
+  topic?: string | null
+}
+
+/**
+ * SEC=1,RunOnDay=11,RunOnWeek=12,RunOnMonth=13,Custom=21
+ * @format int32
+ */
+export type TaskInterval = 1 | 11 | 12 | 13 | 21
+
+export interface TaskListOutput {
+  /** 主键 */
+  id?: string | null
+  /** 任务标题 */
+  topic?: string | null
+  /** 任务数据 */
+  body?: string | null
+  /**
+   * 任务执行多少轮
+   * @format int32
+   */
+  round?: number
+  /** SEC=1,RunOnDay=11,RunOnWeek=12,RunOnMonth=13,Custom=21 */
+  interval?: TaskInterval
+  /** 定时参数值 */
+  intervalArgument?: string | null
+  /** Running=0,Paused=1,Completed=2 */
+  status?: TaskStatus
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createTime?: string
+  /**
+   * 最后运行时间
+   * @format date-time
+   */
+  lastRunTime?: string
+  /**
+   * 当前运行到第几轮
+   * @format int32
+   */
+  currentRound?: number
+  /**
+   * 错次数
+   * @format int32
+   */
+  errorTimes?: number
+}
+
+export interface TaskLog {
+  taskId?: string | null
+  /** @format int32 */
+  round?: number
+  /** @format int64 */
+  elapsedMilliseconds?: number
+  success?: boolean
+  exception?: string | null
+  remark?: string | null
+  /** @format date-time */
+  createTime?: string
+}
+
+export interface TaskLogGetPageDto {
+  taskId?: string | null
+}
+
+/**
+ * Running=0,Paused=1,Completed=2
+ * @format int32
+ */
+export type TaskStatus = 0 | 1 | 2
+
+/** 修改 */
+export interface TaskUpdateInput {
+  /** 任务标题 */
+  topic?: string | null
+  /** 任务数据 */
+  body?: string | null
+  /**
+   * 任务执行多少轮,-1为永久循环
+   * @format int32
+   */
+  round?: number
+  /** SEC=1,RunOnDay=11,RunOnWeek=12,RunOnMonth=13,Custom=21 */
+  interval?: TaskInterval
+  /** 定时参数值 60,60,60,120,120,1200,1200 */
+  intervalArgument?: string | null
+  /**
+   * 任务Id
+   * @minLength 1
+   */
+  id: string
+}
+
+/** 添加 */
+export interface TenantAddInput {
+  /**
+   * 企业名称
+   * @minLength 1
+   */
+  name: string
+  /**
+   * 编码
+   * @minLength 1
+   */
+  code: string
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  realName: string
+  /**
+   * 手机号码
+   * @minLength 1
+   */
+  phone: string
+  /** 邮箱地址 */
+  email?: string | null
+  /** 数据库注册键 */
+  dbKey?: string | null
+  /** MySql=0,SqlServer=1,PostgreSQL=2,Oracle=3,Sqlite=4,OdbcOracle=5,OdbcSqlServer=6,OdbcMySql=7,OdbcPostgreSQL=8,Odbc=9,OdbcDameng=10,MsAccess=11,Dameng=12,OdbcKingbaseES=13,ShenTong=14,KingbaseES=15,Firebird=16,Custom=17,ClickHouse=18,GBase=19,CustomOracle=20,CustomSqlServer=21,CustomMySql=22,CustomPostgreSQL=23 */
+  dbType?: DataType
+  /** 连接字符串 */
+  connectionString?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /** 说明 */
+  description?: string | null
+}
+
+/** 租户 */
+export interface TenantEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /** 企业名称 */
+  name?: string | null
+  /** 编码 */
+  code?: string | null
+  /** 姓名 */
+  realName?: string | null
+  /** 手机号码 */
+  phone?: string | null
+  /** 邮箱地址 */
+  email?: string | null
+  /**
+   * 授权用户
+   * @format int64
+   */
+  userId?: number | null
+  /** 用户 */
+  user?: UserEntity
+  /** 租户类型:Platform=1,Tenant=2 */
+  tenantType?: TenantType
+  /** 数据库注册键 */
+  dbKey?: string | null
+  /** MySql=0,SqlServer=1,PostgreSQL=2,Oracle=3,Sqlite=4,OdbcOracle=5,OdbcSqlServer=6,OdbcMySql=7,OdbcPostgreSQL=8,Odbc=9,OdbcDameng=10,MsAccess=11,Dameng=12,OdbcKingbaseES=13,ShenTong=14,KingbaseES=15,Firebird=16,Custom=17,ClickHouse=18,GBase=19,CustomOracle=20,CustomSqlServer=21,CustomMySql=22,CustomPostgreSQL=23 */
+  dbType?: DataType
+  /** 连接字符串 */
+  connectionString?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /** 说明 */
+  description?: string | null
+}
+
+export interface TenantGetOutput {
+  /**
+   * 企业名称
+   * @minLength 1
+   */
+  name: string
+  /**
+   * 编码
+   * @minLength 1
+   */
+  code: string
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  realName: string
+  /**
+   * 手机号码
+   * @minLength 1
+   */
+  phone: string
+  /** 邮箱地址 */
+  email?: string | null
+  /** 数据库注册键 */
+  dbKey?: string | null
+  /** MySql=0,SqlServer=1,PostgreSQL=2,Oracle=3,Sqlite=4,OdbcOracle=5,OdbcSqlServer=6,OdbcMySql=7,OdbcPostgreSQL=8,Odbc=9,OdbcDameng=10,MsAccess=11,Dameng=12,OdbcKingbaseES=13,ShenTong=14,KingbaseES=15,Firebird=16,Custom=17,ClickHouse=18,GBase=19,CustomOracle=20,CustomSqlServer=21,CustomMySql=22,CustomPostgreSQL=23 */
+  dbType?: DataType
+  /** 连接字符串 */
+  connectionString?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /** 说明 */
+  description?: string | null
+  /**
+   * 接口Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface TenantGetPageDto {
+  /** 企业名称 */
+  name?: string | null
+}
+
+export interface TenantListOutput {
+  /**
+   * 主键
+   * @format int64
+   */
+  id?: number
+  /** 企业编码 */
+  code?: string | null
+  /** 企业名称 */
+  name?: string | null
+  /** 姓名 */
+  realName?: string | null
+  /** 手机号码 */
+  phone?: string | null
+  /** 邮箱地址 */
+  email?: string | null
+  /** MySql=0,SqlServer=1,PostgreSQL=2,Oracle=3,Sqlite=4,OdbcOracle=5,OdbcSqlServer=6,OdbcMySql=7,OdbcPostgreSQL=8,Odbc=9,OdbcDameng=10,MsAccess=11,Dameng=12,OdbcKingbaseES=13,ShenTong=14,KingbaseES=15,Firebird=16,Custom=17,ClickHouse=18,GBase=19,CustomOracle=20,CustomSqlServer=21,CustomMySql=22,CustomPostgreSQL=23 */
+  dbType?: DataType
+  /** 数据库名称 */
+  dbTypeName?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /** 说明 */
+  description?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+}
+
+/**
+ * 租户类型:Platform=1,Tenant=2
+ * @format int32
+ */
+export type TenantType = 1 | 2
+
+/** 修改 */
+export interface TenantUpdateInput {
+  /**
+   * 企业名称
+   * @minLength 1
+   */
+  name: string
+  /**
+   * 编码
+   * @minLength 1
+   */
+  code: string
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  realName: string
+  /**
+   * 手机号码
+   * @minLength 1
+   */
+  phone: string
+  /** 邮箱地址 */
+  email?: string | null
+  /** 数据库注册键 */
+  dbKey?: string | null
+  /** MySql=0,SqlServer=1,PostgreSQL=2,Oracle=3,Sqlite=4,OdbcOracle=5,OdbcSqlServer=6,OdbcMySql=7,OdbcPostgreSQL=8,Odbc=9,OdbcDameng=10,MsAccess=11,Dameng=12,OdbcKingbaseES=13,ShenTong=14,KingbaseES=15,Firebird=16,Custom=17,ClickHouse=18,GBase=19,CustomOracle=20,CustomSqlServer=21,CustomMySql=22,CustomPostgreSQL=23 */
+  dbType?: DataType
+  /** 连接字符串 */
+  connectionString?: string | null
+  /** 启用 */
+  enabled?: boolean
+  /** 说明 */
+  description?: string | null
+  /**
+   * 接口Id
+   * @format int64
+   */
+  id: number
+}
+
+/** 添加 */
+export interface UserAddInput {
+  /**
+   * 账号
+   * @minLength 1
+   */
+  userName: string
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  name: string
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /** 角色Ids */
+  roleIds?: number[] | null
+  /** 所属部门Ids */
+  orgIds?: number[] | null
+  /**
+   * 主属部门Id
+   * @format int64
+   */
+  orgId?: number
+  /**
+   * 直属主管Id
+   * @format int64
+   */
+  managerUserId?: number | null
+  /** 直属主管姓名 */
+  managerUserName?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+  /** 员工添加 */
+  staff?: StaffAddInput
+  /**
+   * 密码
+   * @minLength 1
+   */
+  password: string
+  /** 用户状态:Enabled=0,Disabled=1,WaitChangePasssword=2,WaitActive=3 */
+  status?: UserStatus
+}
+
+/** 添加会员 */
+export interface UserAddMemberInput {
+  /**
+   * 账号
+   * @minLength 1
+   */
+  userName: string
+  /** 姓名 */
+  name?: string | null
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+  /**
+   * 密码
+   * @minLength 1
+   */
+  password: string
+  /** 用户状态:Enabled=0,Disabled=1,WaitChangePasssword=2,WaitActive=3 */
+  status?: UserStatus
+}
+
+/** 修改密码 */
+export interface UserChangePasswordInput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 旧密码
+   * @minLength 1
+   */
+  oldPassword: string
+  /**
+   * 新密码
+   * @minLength 1
+   */
+  newPassword: string
+  /**
+   * 确认新密码
+   * @minLength 1
+   */
+  confirmPassword: string
+}
+
+/** 用户 */
+export interface UserEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 租户Id
+   * @format int64
+   */
+  tenantId?: number | null
+  /** 租户 */
+  tenant?: TenantEntity
+  /** 账号 */
+  userName?: string | null
+  /** 密码 */
+  password?: string | null
+  /** 姓名 */
+  name?: string | null
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /**
+   * 主属部门Id
+   * @format int64
+   */
+  orgId?: number
+  /** 组织架构 */
+  org?: OrgEntity
+  /**
+   * 直属主管Id
+   * @format int64
+   */
+  managerUserId?: number | null
+  /** 用户 */
+  managerUser?: UserEntity
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+  /** 用户状态:Enabled=0,Disabled=1,WaitChangePasssword=2,WaitActive=3 */
+  status?: UserStatus
+  /** 用户类型:Member=0,DefaultUser=1,TenantAdmin=10,PlatformAdmin=100 */
+  type?: UserType
+  /** 角色列表 */
+  roles?: RoleEntity[] | null
+  /** 部门列表 */
+  orgs?: OrgEntity[] | null
+  /** 用户员工 */
+  staff?: UserStaffEntity
+}
+
+export interface UserGetBasicOutput {
+  /** 头像 */
+  avatar?: string | null
+  /** 姓名 */
+  name?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+}
+
+export interface UserGetOrgDto {
+  /** @format int64 */
+  id?: number
+  name?: string | null
+}
+
+export interface UserGetOutput {
+  /**
+   * 账号
+   * @minLength 1
+   */
+  userName: string
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  name: string
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /**
+   * 主属部门Id
+   * @format int64
+   */
+  orgId?: number
+  /**
+   * 直属主管Id
+   * @format int64
+   */
+  managerUserId?: number | null
+  /** 直属主管姓名 */
+  managerUserName?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+  /** 员工添加 */
+  staff?: StaffAddInput
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+  /** 角色列表 */
+  roles?: UserGetRoleDto[] | null
+  /** 部门列表 */
+  orgs?: UserGetOrgDto[] | null
+  /** 所属部门Ids */
+  orgIds?: number[] | null
+  /** 角色Ids */
+  roleIds?: number[] | null
+}
+
+/** 用户分页查询条件 */
+export interface UserGetPageDto {
+  /**
+   * 部门Id
+   * @format int64
+   */
+  orgId?: number | null
+}
+
+export interface UserGetPageOutput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /** 账号 */
+  userName?: string | null
+  /** 姓名 */
+  name?: string | null
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /** 用户类型:Member=0,DefaultUser=1,TenantAdmin=10,PlatformAdmin=100 */
+  type?: UserType
+  /** 角色 */
+  roleNames?: string[] | null
+  /** 是否主管 */
+  isManager?: boolean
+  roles?: RoleEntity[] | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+}
+
+export interface UserGetRoleDto {
+  /** @format int64 */
+  id?: number
+  name?: string | null
+}
+
+export interface UserGetRoleUserListOutput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /** 姓名 */
+  name?: string | null
+}
+
+export interface UserPermissionsOutput {
+  httpMethods?: string | null
+  path?: string | null
+}
+
+/** 重置密码 */
+export interface UserResetPasswordInput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /** 密码 */
+  password?: string | null
+}
+
+/** 设置主管 */
+export interface UserSetManagerInput {
+  /**
+   * 用户Id
+   * @format int64
+   */
+  userId?: number
+  /**
+   * 部门Id
+   * @format int64
+   */
+  orgId?: number
+  /** 是否主管 */
+  isManager?: boolean
+}
+
+/** 用户员工 */
+export interface UserStaffEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 租户Id
+   * @format int64
+   */
+  tenantId?: number | null
+  /** 职位 */
+  position?: string | null
+  /** 工号 */
+  jobNumber?: string | null
+  /** 性别:Unknown=0,Male=1,Female=2 */
+  sex?: Sex
+  /**
+   * 入职时间
+   * @format date-time
+   */
+  entryTime?: string | null
+  /** 个人简介 */
+  introduce?: string | null
+}
+
+/**
+ * 用户状态:Enabled=0,Disabled=1,WaitChangePasssword=2,WaitActive=3
+ * @format int32
+ */
+export type UserStatus = 0 | 1 | 2 | 3
+
+/**
+ * 用户类型:Member=0,DefaultUser=1,TenantAdmin=10,PlatformAdmin=100
+ * @format int32
+ */
+export type UserType = 0 | 1 | 10 | 100
+
+/** 更新基本信息 */
+export interface UserUpdateBasicInput {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  name: string
+  /** 昵称 */
+  nickName?: string | null
+}
+
+/** 修改 */
+export interface UserUpdateInput {
+  /**
+   * 账号
+   * @minLength 1
+   */
+  userName: string
+  /**
+   * 姓名
+   * @minLength 1
+   */
+  name: string
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /** 角色Ids */
+  roleIds?: number[] | null
+  /** 所属部门Ids */
+  orgIds?: number[] | null
+  /**
+   * 主属部门Id
+   * @format int64
+   */
+  orgId?: number
+  /**
+   * 直属主管Id
+   * @format int64
+   */
+  managerUserId?: number | null
+  /** 直属主管姓名 */
+  managerUserName?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+  /** 员工添加 */
+  staff?: StaffAddInput
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+/** 修改会员 */
+export interface UserUpdateMemberInput {
+  /**
+   * 账号
+   * @minLength 1
+   */
+  userName: string
+  /** 姓名 */
+  name?: string | null
+  /** 手机号 */
+  mobile?: string | null
+  /** 邮箱 */
+  email?: string | null
+  /** 昵称 */
+  nickName?: string | null
+  /** 头像 */
+  avatar?: string | null
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id: number
+}
+
+/** 添加 */
+export interface ViewAddInput {
+  /**
+   * 所属节点
+   * @format int64
+   */
+  parentId?: number | null
+  /** 视图命名 */
+  name?: string | null
+  /** 视图名称 */
+  label?: string | null
+  /** 视图路径 */
+  path?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 缓存 */
+  cache?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+}
+
+/** 视图管理 */
+export interface ViewEntity {
+  /**
+   * 主键Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 创建者Id
+   * @format int64
+   */
+  createdUserId?: number | null
+  /**
+   * 创建者
+   * @maxLength 50
+   */
+  createdUserName?: string | null
+  /**
+   * 创建时间
+   * @format date-time
+   */
+  createdTime?: string | null
+  /**
+   * 修改者Id
+   * @format int64
+   */
+  modifiedUserId?: number | null
+  /**
+   * 修改者
+   * @maxLength 50
+   */
+  modifiedUserName?: string | null
+  /**
+   * 修改时间
+   * @format date-time
+   */
+  modifiedTime?: string | null
+  /** 是否删除 */
+  isDeleted?: boolean
+  /**
+   * 所属节点
+   * @format int64
+   */
+  parentId?: number
+  /** 视图命名 */
+  name?: string | null
+  /** 视图名称 */
+  label?: string | null
+  /** 视图路径 */
+  path?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 缓存 */
+  cache?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  childs?: ViewEntity[] | null
+}
+
+export interface ViewGetOutput {
+  /**
+   * 所属节点
+   * @format int64
+   */
+  parentId?: number | null
+  /** 视图命名 */
+  name?: string | null
+  /** 视图名称 */
+  label?: string | null
+  /** 视图路径 */
+  path?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 缓存 */
+  cache?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 视图Id
+   * @format int64
+   */
+  id: number
+}
+
+export interface ViewListOutput {
+  /**
+   * 视图Id
+   * @format int64
+   */
+  id?: number
+  /**
+   * 视图父级
+   * @format int64
+   */
+  parentId?: number | null
+  /** 视图命名 */
+  name?: string | null
+  /** 视图名称 */
+  label?: string | null
+  /** 视图路径 */
+  path?: string | null
+  /** 缓存 */
+  cache?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /** 说明 */
+  description?: string | null
+}
+
+export interface ViewSyncDto {
+  /** 视图命名 */
+  name?: string | null
+  /** 地址 */
+  path?: string | null
+  /** 视图名称 */
+  label?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 缓存 */
+  cache?: boolean
+}
+
+export interface ViewSyncInput {
+  views?: ViewSyncDto[] | null
+}
+
+/** 修改 */
+export interface ViewUpdateInput {
+  /**
+   * 所属节点
+   * @format int64
+   */
+  parentId?: number | null
+  /** 视图命名 */
+  name?: string | null
+  /** 视图名称 */
+  label?: string | null
+  /** 视图路径 */
+  path?: string | null
+  /** 说明 */
+  description?: string | null
+  /** 缓存 */
+  cache?: boolean
+  /**
+   * 排序
+   * @format int32
+   */
+  sort?: number
+  /** 启用 */
+  enabled?: boolean
+  /**
+   * 视图Id
+   * @format int64
+   */
+  id: number
+}

+ 408 - 0
src/api/admin/http-client.ts

@@ -0,0 +1,408 @@
+/* eslint-disable */
+/* tslint:disable */
+/*
+ * ---------------------------------------------------------------
+ * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API        ##
+ * ##                                                           ##
+ * ## AUTHOR: acacode                                           ##
+ * ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
+ * ---------------------------------------------------------------
+ */
+
+import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, HeadersDefaults, ResponseType } from 'axios'
+import { ElLoading, ElMessage, LoadingOptions } from 'element-plus'
+import { Local, Session } from '/@/utils/storage'
+
+export const adminTokenKey = 'admin-token'
+
+// 获得token
+export const getToken = () => {
+  return Local.get(adminTokenKey)
+}
+// 设置token
+export const setToken = (token: any) => {
+  return Local.set(adminTokenKey, token)
+}
+// 清除token
+export const clearToken = () => {
+  Local.remove(adminTokenKey)
+  Session.remove('token')
+  window.requests = []
+  window.location.reload()
+}
+
+export type QueryParamsType = Record<string | number, any>
+
+export interface FullRequestParams extends Omit<AxiosRequestConfig, 'data' | 'params' | 'url' | 'responseType'> {
+  /** set parameter to `true` for call `securityWorker` for this request */
+  secure?: boolean
+  /** request path */
+  path: string
+  /** content type of request body */
+  type?: ContentType
+  /** query params */
+  query?: QueryParamsType
+  /** format of response (i.e. response.json() -> format: "json") */
+  format?: ResponseType
+  /** request body */
+  body?: unknown
+  /** 显示错误消息 */
+  showErrorMessage?: boolean
+  /** 显示成功消息 */
+  showSuccessMessage?: boolean
+  /** 登录访问 */
+  login?: boolean
+  /** 加载中 */
+  loading?: boolean
+  loadingOptions?: LoadingOptions
+  /** 取消重复请求 */
+  cancelRepeatRequest?: boolean
+}
+
+export type RequestParams = Omit<FullRequestParams, 'body' | 'method' | 'query' | 'path'>
+
+export interface ApiConfig<SecurityDataType = unknown> extends Omit<AxiosRequestConfig, 'data' | 'cancelToken'> {
+  securityWorker?: (securityData: SecurityDataType | null) => Promise<AxiosRequestConfig | void> | AxiosRequestConfig | void
+  secure?: boolean
+  format?: ResponseType
+}
+
+export enum ContentType {
+  Json = 'application/json',
+  FormData = 'multipart/form-data',
+  UrlEncoded = 'application/x-www-form-urlencoded',
+  Text = 'text/plain',
+}
+
+export interface LoadingInstance {
+  target: any
+  count: number
+}
+
+const pendingMap = new Map()
+
+const loadingInstance: LoadingInstance = {
+  target: null,
+  count: 0,
+}
+
+export class HttpClient<SecurityDataType = unknown> {
+  public instance: AxiosInstance
+  private securityData: SecurityDataType | null = null
+  private securityWorker?: ApiConfig<SecurityDataType>['securityWorker']
+  private secure?: boolean
+  private format?: ResponseType
+
+  constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig<SecurityDataType> = {}) {
+    this.instance = axios.create({ ...axiosConfig, timeout: 60000, baseURL: axiosConfig.baseURL || 'http://localhost:8000' })
+    this.secure = secure
+    this.format = format
+    this.securityWorker = securityWorker
+  }
+
+  public setSecurityData = (data: SecurityDataType | null) => {
+    this.securityData = data
+  }
+
+  protected mergeRequestParams(params1: AxiosRequestConfig, params2?: AxiosRequestConfig): AxiosRequestConfig {
+    const method = params1.method || (params2 && params2.method)
+
+    return {
+      ...this.instance.defaults,
+      ...params1,
+      ...(params2 || {}),
+      headers: {
+        ...((method && this.instance.defaults.headers[method.toLowerCase() as keyof HeadersDefaults]) || {}),
+        ...(params1.headers || {}),
+        ...((params2 && params2.headers) || {}),
+      },
+    }
+  }
+
+  protected stringifyFormItem(formItem: unknown) {
+    if (typeof formItem === 'object' && formItem !== null) {
+      return JSON.stringify(formItem)
+    } else {
+      return `${formItem}`
+    }
+  }
+
+  protected createFormData(input: Record<string, unknown>): FormData {
+    return Object.keys(input || {}).reduce((formData, key) => {
+      const property = input[key]
+      const propertyContent: any[] = property instanceof Array ? property : [property]
+
+      for (const formItem of propertyContent) {
+        const isFileType = formItem instanceof Blob || formItem instanceof File
+        formData.append(key, isFileType ? formItem : this.stringifyFormItem(formItem))
+      }
+
+      return formData
+    }, new FormData())
+  }
+
+  /**
+   * 错误处理
+   * @param {*} error
+   */
+  protected errorHandle(error: any) {
+    if (!error) {
+      return
+    }
+    if (axios.isCancel(error)) return console.error('请求重复已被自动取消:' + error.message)
+    let message = ''
+    if (error.response) {
+      switch (error.response.status) {
+        case 302:
+          message = '接口重定向'
+          break
+        case 400:
+          message = '参数不正确'
+          break
+        case 401:
+          message = '您还没有登录'
+          break
+        case 403:
+          message = '您没有权限操作'
+          break
+        case 404:
+          message = '请求地址出错:' + error.response.config.url
+          break
+        case 408:
+          message = '请求超时'
+          break
+        case 409:
+          message = '系统已存在相同数据'
+          break
+        case 500:
+          message = '服务器内部错误'
+          break
+        case 501:
+          message = '服务未实现'
+          break
+        case 502:
+          message = '网关错误'
+          break
+        case 503:
+          message = '服务不可用'
+          break
+        case 504:
+          message = '服务暂时无法访问,请稍后再试'
+          break
+        case 505:
+          message = 'HTTP版本不受支持'
+          break
+        default:
+          message = '异常问题,请联系网站管理员'
+          break
+      }
+    }
+    if (error.message.includes('timeout')) message = '请求超时'
+    if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常' : '您已断网'
+
+    if (message) {
+      ElMessage.error({ message })
+    }
+  }
+
+  /**
+   * 刷新token
+   * @param {*} config
+   */
+  protected async refreshToken(config: any) {
+    const token = getToken()
+    if (!token) {
+      clearToken()
+      return Promise.reject(config)
+    }
+
+    if (window.tokenRefreshing) {
+      return new Promise((resolve) => {
+        window.requests.push(() => {
+          resolve(this.instance(config))
+        })
+      })
+    }
+
+    window.tokenRefreshing = true
+
+    return this.request<AxiosResponse, any>({
+      path: `/api/admin/auth/refresh`,
+      method: 'GET',
+      secure: true,
+      format: 'json',
+      login: false,
+      query: {
+        token: token,
+      },
+    })
+      .then((res) => {
+        if (res?.success) {
+          const token = res.data.token
+          setToken(token)
+          window.requests.forEach((apiRequest) => apiRequest())
+          window.requests = []
+          return this.instance(config)
+        } else {
+          clearToken()
+          return Promise.reject(res)
+        }
+      })
+      .catch((error) => {
+        clearToken()
+        return Promise.reject(error)
+      })
+      .finally(() => {
+        window.tokenRefreshing = false
+      })
+  }
+
+  /**
+   * 储存每个请求的唯一cancel回调, 以此为标识
+   */
+  protected addPending(config: AxiosRequestConfig) {
+    const pendingKey = this.getPendingKey(config)
+    config.cancelToken =
+      config.cancelToken ||
+      new axios.CancelToken((cancel) => {
+        if (!pendingMap.has(pendingKey)) {
+          pendingMap.set(pendingKey, cancel)
+        }
+      })
+  }
+
+  /**
+   * 删除重复的请求
+   */
+  protected removePending(config: AxiosRequestConfig) {
+    const pendingKey = this.getPendingKey(config)
+    if (pendingMap.has(pendingKey)) {
+      const cancelToken = pendingMap.get(pendingKey)
+      cancelToken(pendingKey)
+      pendingMap.delete(pendingKey)
+    }
+  }
+
+  /**
+   * 生成每个请求的唯一key
+   */
+  protected getPendingKey(config: AxiosRequestConfig) {
+    let { data } = config
+    const { url, method, params, headers } = config
+    if (typeof data === 'string') data = JSON.parse(data)
+    return [url, method, headers && headers.Authorization ? headers.Authorization : '', JSON.stringify(params), JSON.stringify(data)].join('&')
+  }
+
+  /**
+   * 关闭Loading层实例
+   */
+  protected closeLoading(loading: boolean = false) {
+    if (loading && loadingInstance.count > 0) loadingInstance.count--
+    if (loadingInstance.count === 0) {
+      loadingInstance.target.close()
+      loadingInstance.target = null
+    }
+  }
+
+  public request = async <T = any, _E = any>({
+    secure,
+    path,
+    type,
+    query,
+    format,
+    body,
+    showErrorMessage = true,
+    showSuccessMessage = false,
+    login = true,
+    loading = false,
+    loadingOptions = {},
+    cancelRepeatRequest = false,
+    ...params
+  }: FullRequestParams): Promise<T> => {
+    const secureParams =
+      ((typeof secure === 'boolean' ? secure : this.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {}
+    const requestParams = this.mergeRequestParams(params, secureParams)
+    const responseFormat = format || this.format || undefined
+
+    if (type === ContentType.FormData && body && body !== null && typeof body === 'object') {
+      body = this.createFormData(body as Record<string, unknown>)
+    }
+
+    if (type === ContentType.Text && body && body !== null && typeof body !== 'string') {
+      body = JSON.stringify(body)
+    }
+
+    // 请求拦截
+    this.instance.interceptors.request.use(
+      (config) => {
+        this.removePending(config)
+        cancelRepeatRequest && this.addPending(config)
+
+        if (loading) {
+          loadingInstance.count++
+          if (loadingInstance.count === 1) {
+            loadingInstance.target = ElLoading.service(loadingOptions)
+          }
+        }
+
+        const accessToken = getToken()
+        config.headers!['Authorization'] = `Bearer ${accessToken}`
+        return config
+      },
+      (error) => {
+        return Promise.reject(error)
+      }
+    )
+    // 响应拦截
+    this.instance.interceptors.response.use(
+      (res) => {
+        this.removePending(res.config)
+        loading && this.closeLoading(loading)
+
+        const data = res.data
+        if (data.success) {
+          if (showSuccessMessage) {
+            ElMessage.success({ message: data.msg ? data.msg : '操作成功' })
+          }
+        } else {
+          if (showErrorMessage) {
+            ElMessage.error({ message: data.msg ? data.msg : '操作失败' })
+          }
+          // return Promise.reject(res)
+        }
+
+        return res
+      },
+      async (error) => {
+        error.config && this.removePending(error.config)
+        loading && this.closeLoading(loading)
+
+        //刷新token
+        if (login && error?.response?.status === 401) {
+          return this.refreshToken(error.config)
+        }
+
+        //错误处理
+        if (showErrorMessage) {
+          this.errorHandle(error)
+        }
+
+        return Promise.reject(error)
+      }
+    )
+
+    return this.instance
+      .request({
+        ...requestParams,
+        headers: {
+          ...(requestParams.headers || {}),
+          ...(type && type !== ContentType.FormData ? { 'Content-Type': type } : {}),
+        },
+        params: query,
+        responseType: responseFormat,
+        data: body,
+        url: path,
+      })
+      .then((response) => response.data)
+  }
+}

+ 27 - 0
src/api/login/index.ts

@@ -0,0 +1,27 @@
+import request from '/@/utils/request'
+
+/**
+ * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
+ *
+ * 登录api接口集合
+ * @method signIn 用户登录
+ * @method signOut 用户退出登录
+ */
+export function useLoginApi() {
+  return {
+    signIn: (data: object) => {
+      return request({
+        url: '/user/signIn',
+        method: 'post',
+        data,
+      })
+    },
+    signOut: (data: object) => {
+      return request({
+        url: '/user/signOut',
+        method: 'post',
+        data,
+      })
+    },
+  }
+}

+ 30 - 0
src/api/menu/index.ts

@@ -0,0 +1,30 @@
+import request from '/@/utils/request'
+
+/**
+ * 以下为模拟接口地址,gitee 的不通,就换自己的真实接口地址
+ *
+ * (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
+ *
+ * 后端控制菜单模拟json,路径在 https://gitee.com/lyt-top/vue-next-admin-images/tree/master/menu
+ * 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
+ * @method getAdminMenu 获取后端动态路由菜单(admin)
+ * @method getTestMenu 获取后端动态路由菜单(test)
+ */
+export function useMenuApi() {
+  return {
+    getAdminMenu: (params?: object) => {
+      return request({
+        url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/adminMenu.json',
+        method: 'get',
+        params,
+      })
+    },
+    getTestMenu: (params?: object) => {
+      return request({
+        url: '/gitee/lyt-top/vue-next-admin-images/raw/master/menu/testMenu.json',
+        method: 'get',
+        params,
+      })
+    },
+  }
+}

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


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


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


+ 33 - 0
src/assets/logo-mini.svg

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="120px" height="120px" viewBox="0 0 120 120" enable-background="new 0 0 120 120" xml:space="preserve">  <image id="image0" width="120" height="120" x="0" y="0"
+    href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAMAAAAOusbgAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABjFBMVEUYkP9ruP/K5v+/4P8+
+ov/n9P/////9/v+t1/+VzP/4/P/7/f+12/8xnP9suP/q9f/p9f93vv+h0v/B4f9LqP+Xzf/x+P+L
+x/+l1P/i8f9Ip/9qt//A4f/n8/9Urf/C4v/e7/86of/Z7f/E4/+p1v9esv/r9f/z+f/6/P+Oyf/1
++v96v/+Qyv+63v/V6//G5P9jtP/2+/+m1P8klv/+//9Xrv/o9P+k0/+Pyf/b7v9ltf+Awv/g8P+Q
+yf+93/9yu//f8P/5/P/0+v/8/v9xu//R6f+e0P/Y7P98wP+Sy/+c0P97v/+Gxf+x2v/d7v+23P+7
+3v/v9/+Dw//3+//S6f9Yr/9asP+v2f+t2P/l8v+ExP9+wf/c7v+Ty//6/f9otv/T6v+u2P/w+P+5
+3f9/wv+r1/9csf+n1f/j8v+02/+w2f/J5f+Ixv/t9v/h8f+43f+Ryv+Wzf+83//m8//K5f/Q6P9h
+s/92vf/W6/+s1/+Hxf/D4v/d7/+az/9zvP/U6v+j0/+Kx//y+f9vuv+c52N2AAAAAWJLR0QGYWa4
+fQAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB+YMCw8jGc+moKMAAAPRSURBVGje7ZrpX9Mw
+GMfLlSmDDRjMMWBjDCnHOFtAEOWYggIeA0TUgYioyCGiiPf9j9vStUmPbSxdn/jR/l6xdMuXJE+e
+IynHuXLlypUrV65cFaiS0rJyJtwK5DnHgHu+EiHkraoGB/v8SFZNLTi5znNK9gbqgcENXqQoeAEW
+HEKqGsOMwMEmUHCzBm6JgIKjGrg1xgjcBsrlSlmB2xiBw40aOA4KjrRo4HZQ8MUOlettAAX7eBXc
+2cUI3N3DCMz75M/hRG+fpH6f02C8xrxvINQ2qE38kOAsGFu16EU6DVY5GqGHR1BWDY46OGq8xhYS
+A87lYpcqUS7y2Lgz2MsTIsot/goTrOzQrhZ7uiencmCnZ5LadFdcKya2PuDNwkwGr89GuFgrbpmj
+LnFu3DQ0CPPBLDsovqDM7CJhdOItSm6kJXl7Utdyx3KWZ+K12sa9Sz5IUdr20rL0X6/cywcm4/Gq
+X7ermunAXZ3yryvvY/Nc85i5neTGqdN/gTIBVcBIfPAQz/6jNRwQFJFhMZbWP4vaGfHplOmcbyQR
+mMC2zRORcH0j+yoUIOyPk0PGZ1ErsHHAtFlRE947qcdZwUQFo02Rau6ULoTIJEXjkK3AwqbB7mjr
+SGEM97FyBvCiIWKJT+i4ZJmCtgy1qEXNZhrwFnX9OoT9hfg0G1ipYITtZ82GFTb+pAD1dONuDOal
+Az9/sTNt9iwmgzy7iALJuGAYPPUyiKxEHSJkkS5f7/6iKJ/8qzbApJ3qK4a8YLPPKUQ6V7RbELjU
+XopLRps5sqvd3Fhxz2ZWT8ZXIqwL+we5wX22D3bb8ZBxPeoby5Nm+u3XbvWvcHeHmaas+Z6qHTsG
+reo19kdpeUOZ8z2x5ij8hpyCt8XgcsKx1uE7wSqr5o+k5tUK3NBRpHz6pEPt8KS8a888y4qvfp+S
+/5YMQkwXZbyyEooXkXxms5VNfVC+VZLe+Tjw6XOimEe5o6fLLMXdOoskkzahO5PmpQQbeXsFoSxp
+BoccBHMlX0QlkaleCBzwvH/v6zEMmOO+9X/fJz9HocBGaWDxByMw8MHeXwCmTdppdah5TMcPEfUK
+/XfgWQ8jsFYYAt+zcT/V0gH4ng0X7SPDsGCtaIeeaq1oB75ZxPUc8AUfvuIDB8dZgdUoAXuzKKkd
+INezlOqsd+13VZhUnwk+YvVgBhw8nlLAlEfS9MpcPnhmocGZF0GWl8DByss+0HkApy4y7X2HDSln
+ppsOXxdbSS5aGdiWlAtsIfTrNwMwtwKfcSla34B+iSyjWFo9AIHWUnqbDdiVK1euXP3r+gNkDo0T
+b9cEiwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMi0xMi0xMVQxNDozNToyNSswMTowMJANoQ0AAAAl
+dEVYdGRhdGU6bW9kaWZ5ADIwMjItMTItMTFUMTQ6MzU6MjUrMDE6MDDhUBmxAAAAAElFTkSuQmCC" />
+</svg>

+ 26 - 0
src/components/auth/auth.vue

@@ -0,0 +1,26 @@
+<template>
+  <slot v-if="getUserAuthBtnList" />
+</template>
+
+<script setup lang="ts" name="auth">
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useUserInfo } from '/@/stores/userInfo'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  value: {
+    type: String,
+    default: () => '',
+  },
+})
+
+// 定义变量内容
+const stores = useUserInfo()
+const { userInfos } = storeToRefs(stores)
+
+// 获取 pinia 中的用户权限
+const getUserAuthBtnList = computed(() => {
+  return userInfos.value.authBtnList.some((v: string) => v === props.value)
+})
+</script>

+ 27 - 0
src/components/auth/authAll.vue

@@ -0,0 +1,27 @@
+<template>
+  <slot v-if="getUserAuthBtnList" />
+</template>
+
+<script setup lang="ts" name="authAll">
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useUserInfo } from '/@/stores/userInfo'
+import { judementSameArr } from '/@/utils/arrayOperation'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  value: {
+    type: Array,
+    default: () => [],
+  },
+})
+
+// 定义变量内容
+const stores = useUserInfo()
+const { userInfos } = storeToRefs(stores)
+
+// 获取 pinia 中的用户权限
+const getUserAuthBtnList = computed(() => {
+  return judementSameArr(props.value, userInfos.value.authBtnList)
+})
+</script>

+ 32 - 0
src/components/auth/auths.vue

@@ -0,0 +1,32 @@
+<template>
+  <slot v-if="getUserAuthBtnList" />
+</template>
+
+<script setup lang="ts" name="auths">
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useUserInfo } from '/@/stores/userInfo'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  value: {
+    type: Array,
+    default: () => [],
+  },
+})
+
+// 定义变量内容
+const stores = useUserInfo()
+const { userInfos } = storeToRefs(stores)
+
+// 获取 pinia 中的用户权限
+const getUserAuthBtnList = computed(() => {
+  let flag = false
+  userInfos.value.authBtnList.map((val: string) => {
+    props.value.map((v) => {
+      if (val === v) flag = true
+    })
+  })
+  return flag
+})
+</script>

+ 143 - 0
src/components/cropper/index.vue

@@ -0,0 +1,143 @@
+<template>
+  <div>
+    <el-dialog title="更换头像" v-model="state.isShowDialog" width="769px">
+      <div class="cropper-warp">
+        <div class="cropper-warp-left">
+          <img :src="state.cropperImg" class="cropper-warp-left-img" />
+        </div>
+        <div class="cropper-warp-right">
+          <div class="cropper-warp-right-title">预览</div>
+          <div class="cropper-warp-right-item">
+            <div class="cropper-warp-right-value">
+              <img :src="state.cropperImgBase64" class="cropper-warp-right-value-img" />
+            </div>
+            <div class="cropper-warp-right-label">100 x 100</div>
+          </div>
+          <div class="cropper-warp-right-item">
+            <div class="cropper-warp-right-value">
+              <img :src="state.cropperImgBase64" class="cropper-warp-right-value-img cropper-size" />
+            </div>
+            <div class="cropper-warp-right-label">50 x 50</div>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="onCancel" size="default">取 消</el-button>
+          <el-button type="primary" @click="onSubmit" size="default">更 换</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts" name="cropper">
+import { reactive, nextTick } from 'vue'
+import Cropper from 'cropperjs'
+import 'cropperjs/dist/cropper.css'
+
+// 定义变量内容
+const state = reactive({
+  isShowDialog: false,
+  cropperImg: '',
+  cropperImgBase64: '',
+  cropper: '' as RefType,
+})
+
+// 打开弹窗
+const openDialog = (imgs: string) => {
+  state.cropperImg = imgs
+  state.isShowDialog = true
+  nextTick(() => {
+    initCropper()
+  })
+}
+// 关闭弹窗
+const closeDialog = () => {
+  state.isShowDialog = false
+}
+// 取消
+const onCancel = () => {
+  closeDialog()
+}
+// 更换
+const onSubmit = () => {
+  // state.cropperImgBase64 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg');
+}
+// 初始化cropperjs图片裁剪
+const initCropper = () => {
+  const letImg = <HTMLImageElement>document.querySelector('.cropper-warp-left-img')
+  state.cropper = new Cropper(letImg, {
+    viewMode: 1,
+    dragMode: 'none',
+    initialAspectRatio: 1,
+    aspectRatio: 1,
+    preview: '.before',
+    background: false,
+    autoCropArea: 0.6,
+    zoomOnWheel: false,
+    crop: () => {
+      state.cropperImgBase64 = state.cropper.getCroppedCanvas().toDataURL('image/jpeg')
+    },
+  })
+}
+
+// 暴露变量
+defineExpose({
+  openDialog,
+})
+</script>
+
+<style scoped lang="scss">
+.cropper-warp {
+  display: flex;
+  .cropper-warp-left {
+    position: relative;
+    display: inline-block;
+    height: 350px;
+    flex: 1;
+    border: 1px solid var(--el-border-color);
+    background: var(--el-color-white);
+    overflow: hidden;
+    background-repeat: no-repeat;
+    cursor: move;
+    border-radius: var(--el-border-radius-base);
+    .cropper-warp-left-img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  .cropper-warp-right {
+    width: 150px;
+    height: 350px;
+    .cropper-warp-right-title {
+      text-align: center;
+      height: 20px;
+      line-height: 20px;
+    }
+    .cropper-warp-right-item {
+      margin: 15px 0;
+      .cropper-warp-right-value {
+        display: flex;
+        .cropper-warp-right-value-img {
+          width: 100px;
+          height: 100px;
+          border-radius: var(--el-border-radius-circle);
+          margin: auto;
+        }
+        .cropper-size {
+          width: 50px;
+          height: 50px;
+        }
+      }
+      .cropper-warp-right-label {
+        text-align: center;
+        font-size: 12px;
+        color: var(--el-text-color-primary);
+        height: 30px;
+        line-height: 30px;
+      }
+    }
+  }
+}
+</style>

+ 101 - 0
src/components/editor/index.vue

@@ -0,0 +1,101 @@
+<template>
+  <div class="editor-container">
+    <Toolbar :editor="editorRef" :mode="mode" />
+    <Editor
+      :mode="mode"
+      :defaultConfig="state.editorConfig"
+      :style="{ height }"
+      v-model="state.editorVal"
+      @onCreated="handleCreated"
+      @onChange="handleChange"
+    />
+  </div>
+</template>
+
+<script setup lang="ts" name="wngEditor">
+// https://www.wangeditor.com/v5/for-frame.html#vue3
+import '@wangeditor/editor/dist/css/style.css'
+import { reactive, shallowRef, watch, onBeforeUnmount } from 'vue'
+import { IDomEditor } from '@wangeditor/editor'
+import { Toolbar, Editor } from '@wangeditor/editor-for-vue'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  // 是否禁用
+  disable: {
+    type: Boolean,
+    default: () => false,
+  },
+  // 内容框默认 placeholder
+  placeholder: {
+    type: String,
+    default: () => '请输入内容...',
+  },
+  // https://www.wangeditor.com/v5/getting-started.html#mode-%E6%A8%A1%E5%BC%8F
+  // 模式,可选 <default|simple>,默认 default
+  mode: {
+    type: String,
+    default: () => 'default',
+  },
+  // 高度
+  height: {
+    type: String,
+    default: () => '310px',
+  },
+  // 双向绑定,用于获取 editor.getHtml()
+  getHtml: String,
+  // 双向绑定,用于获取 editor.getText()
+  getText: String,
+})
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['update:getHtml', 'update:getText'])
+
+// 定义变量内容
+const editorRef = shallowRef()
+const state = reactive({
+  editorConfig: {
+    placeholder: props.placeholder,
+  },
+  editorVal: props.getHtml,
+})
+
+// 编辑器回调函数
+const handleCreated = (editor: IDomEditor) => {
+  editorRef.value = editor
+}
+// 编辑器内容改变时
+const handleChange = (editor: IDomEditor) => {
+  emit('update:getHtml', editor.getHtml())
+  emit('update:getText', editor.getText())
+}
+// 页面销毁时
+onBeforeUnmount(() => {
+  const editor = editorRef.value
+  if (editor == null) return
+  editor.destroy()
+})
+// 监听是否禁用改变
+// https://gitee.com/lyt-top/vue-next-admin/issues/I4LM7I
+watch(
+  () => props.disable,
+  (bool) => {
+    const editor = editorRef.value
+    if (editor == null) return
+    bool ? editor.disable() : editor.enable()
+  },
+  {
+    deep: true,
+  }
+)
+// 监听双向绑定值改变,用于回显
+watch(
+  () => props.getHtml,
+  (val) => {
+    state.editorVal = val
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 241 - 0
src/components/iconSelector/index.vue

@@ -0,0 +1,241 @@
+<template>
+  <div class="icon-selector w100 h100">
+    <el-input
+      v-model="state.fontIconSearch"
+      :placeholder="state.fontIconPlaceholder"
+      :clearable="clearable"
+      :disabled="disabled"
+      :size="size"
+      ref="inputWidthRef"
+      @clear="onClearFontIcon"
+      @focus="onIconFocus"
+      @blur="onIconBlur"
+    >
+      <template #prepend>
+        <SvgIcon
+          :name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
+          class="font14"
+          v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
+        />
+        <i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
+      </template>
+    </el-input>
+    <el-popover
+      placement="bottom"
+      :width="state.fontIconWidth"
+      transition="el-zoom-in-top"
+      popper-class="icon-selector-popper"
+      trigger="click"
+      :virtual-ref="inputWidthRef"
+      virtual-triggering
+    >
+      <template #default>
+        <div class="icon-selector-warp">
+          <div class="icon-selector-warp-title">{{ title }}</div>
+          <el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
+            <el-tab-pane lazy label="ali" name="ali">
+              <IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
+            </el-tab-pane>
+            <el-tab-pane lazy label="ele" name="ele">
+              <IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
+            </el-tab-pane>
+            <el-tab-pane lazy label="awe" name="awe">
+              <IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
+            </el-tab-pane>
+          </el-tabs>
+        </div>
+      </template>
+    </el-popover>
+  </div>
+</template>
+
+<script setup lang="ts" name="iconSelector">
+import { defineAsyncComponent, ref, reactive, onMounted, nextTick, computed, watch } from 'vue'
+import type { TabsPaneContext } from 'element-plus'
+import initIconfont from '/@/utils/getStyleSheets'
+import '/@/theme/iconSelector.scss'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  // 输入框前置内容
+  prepend: {
+    type: String,
+    default: () => 'ele-Pointer',
+  },
+  // 输入框占位文本
+  placeholder: {
+    type: String,
+    default: () => '请输入内容搜索图标或者选择图标',
+  },
+  // 输入框占位文本
+  size: {
+    type: String,
+    default: () => 'default',
+  },
+  // 弹窗标题
+  title: {
+    type: String,
+    default: () => '请选择图标',
+  },
+  // 禁用
+  disabled: {
+    type: Boolean,
+    default: () => false,
+  },
+  // 是否可清空
+  clearable: {
+    type: Boolean,
+    default: () => true,
+  },
+  // 自定义空状态描述文字
+  emptyDescription: {
+    type: String,
+    default: () => '无相关图标',
+  },
+  // 双向绑定值,默认为 modelValue,
+  // 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
+  // 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
+  modelValue: String,
+})
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['update:modelValue', 'get', 'clear'])
+
+// 引入组件
+const IconList = defineAsyncComponent(() => import('/@/components/iconSelector/list.vue'))
+
+// 定义变量内容
+const inputWidthRef = ref()
+const state = reactive({
+  fontIconPrefix: '',
+  fontIconWidth: 0,
+  fontIconSearch: '',
+  fontIconPlaceholder: '',
+  fontIconTabActive: 'ali',
+  fontIconList: {
+    ali: [],
+    ele: [],
+    awe: [],
+  },
+})
+
+// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
+const onIconFocus = () => {
+  if (!props.modelValue) return false
+  state.fontIconSearch = ''
+  state.fontIconPlaceholder = props.modelValue
+}
+// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
+const onIconBlur = () => {
+  const list = fontIconTabNameList()
+  setTimeout(() => {
+    const icon = list.filter((icon: string) => icon === state.fontIconSearch)
+    if (icon.length <= 0) state.fontIconSearch = ''
+  }, 300)
+}
+// 图标搜索及图标数据显示
+const fontIconSheetsFilterList = computed(() => {
+  const list = fontIconTabNameList()
+  if (!state.fontIconSearch) return list
+  let search = state.fontIconSearch.trim().toLowerCase()
+  return list.filter((item: string) => {
+    if (item.toLowerCase().indexOf(search) !== -1) return item
+  })
+})
+// 根据 tab name 类型设置图标
+const fontIconTabNameList = () => {
+  let iconList: any = []
+  if (state.fontIconTabActive === 'ali') iconList = state.fontIconList.ali
+  else if (state.fontIconTabActive === 'ele') iconList = state.fontIconList.ele
+  else if (state.fontIconTabActive === 'awe') iconList = state.fontIconList.awe
+  return iconList
+}
+// 处理 icon 双向绑定数值回显
+const initModeValueEcho = () => {
+  if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder)
+  ;(<string | undefined>state.fontIconPlaceholder) = props.modelValue
+  ;(<string | undefined>state.fontIconPrefix) = props.modelValue
+}
+// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
+const initFontIconName = () => {
+  let name = 'ali'
+  if (props.modelValue!.indexOf('iconfont') > -1) name = 'ali'
+  else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele'
+  else if (props.modelValue!.indexOf('fa') > -1) name = 'awe'
+  // 初始化 tab 高亮回显
+  state.fontIconTabActive = name
+  return name
+}
+// 初始化数据
+const initFontIconData = async (name: string) => {
+  if (name === 'ali') {
+    // 阿里字体图标使用 `iconfont xxx`
+    if (state.fontIconList.ali.length > 0) return
+    await initIconfont.ali().then((res: any) => {
+      state.fontIconList.ali = res.map((i: string) => `iconfont ${i}`)
+    })
+  } else if (name === 'ele') {
+    // element plus 图标
+    if (state.fontIconList.ele.length > 0) return
+    await initIconfont.ele().then((res: any) => {
+      state.fontIconList.ele = res
+    })
+  } else if (name === 'awe') {
+    // fontawesome字体图标使用 `fa xxx`
+    if (state.fontIconList.awe.length > 0) return
+    await initIconfont.awe().then((res: any) => {
+      state.fontIconList.awe = res.map((i: string) => `fa ${i}`)
+    })
+  }
+  // 初始化 input 的 placeholder
+  // 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
+  state.fontIconPlaceholder = props.placeholder
+  // 初始化双向绑定回显
+  initModeValueEcho()
+}
+// 图标点击切换
+const onIconClick = (pane: TabsPaneContext) => {
+  initFontIconData(pane.paneName as string)
+  inputWidthRef.value.focus()
+}
+// 获取当前点击的 icon 图标
+const onColClick = (v: string) => {
+  state.fontIconPlaceholder = v
+  state.fontIconPrefix = v
+  emit('get', state.fontIconPrefix)
+  emit('update:modelValue', state.fontIconPrefix)
+  inputWidthRef.value.focus()
+}
+// 清空当前点击的 icon 图标
+const onClearFontIcon = () => {
+  state.fontIconPrefix = ''
+  emit('clear', state.fontIconPrefix)
+  emit('update:modelValue', state.fontIconPrefix)
+}
+// 获取 input 的宽度
+const getInputWidth = () => {
+  nextTick(() => {
+    state.fontIconWidth = inputWidthRef.value.$el.offsetWidth
+  })
+}
+// 监听页面宽度改变
+const initResize = () => {
+  window.addEventListener('resize', () => {
+    getInputWidth()
+  })
+}
+// 页面加载时
+onMounted(() => {
+  initFontIconData(initFontIconName())
+  initResize()
+  getInputWidth()
+})
+// 监听双向绑定 modelValue 的变化
+watch(
+  () => props.modelValue,
+  () => {
+    initModeValueEcho()
+    initFontIconName()
+  }
+)
+</script>

+ 84 - 0
src/components/iconSelector/list.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="icon-selector-warp-row">
+    <el-scrollbar ref="selectorScrollbarRef">
+      <el-row :gutter="10" v-if="props.list.length > 0">
+        <el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" v-for="(v, k) in list" :key="k" @click="onColClick(v)">
+          <div class="icon-selector-warp-item" :class="{ 'icon-selector-active': prefix === v }">
+            <SvgIcon :name="v" />
+          </div>
+        </el-col>
+      </el-row>
+      <el-empty :image-size="100" v-if="list.length <= 0" :description="empty"></el-empty>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script setup lang="ts" name="iconSelectorList">
+// 定义父组件传过来的值
+const props = defineProps({
+  // 图标列表数据
+  list: {
+    type: Array,
+    default: () => [],
+  },
+  // 自定义空状态描述文字
+  empty: {
+    type: String,
+    default: () => '无相关图标',
+  },
+  // 高亮当前选中图标
+  prefix: {
+    type: String,
+    default: () => '',
+  },
+})
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['get-icon'])
+
+// 当前 icon 图标点击时
+const onColClick = (v: unknown | string) => {
+  emit('get-icon', v)
+}
+</script>
+
+<style scoped lang="scss">
+.icon-selector-warp-row {
+  height: 230px;
+  overflow: hidden;
+  .el-row {
+    padding: 15px;
+  }
+  .el-scrollbar__bar.is-horizontal {
+    display: none;
+  }
+  .icon-selector-warp-item {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border: 1px solid var(--el-border-color);
+    border-radius: 5px;
+    margin-bottom: 10px;
+    height: 30px;
+    i {
+      font-size: 20px;
+      color: var(--el-text-color-regular);
+    }
+    &:hover {
+      cursor: pointer;
+      background-color: var(--el-color-primary-light-9);
+      border: 1px solid var(--el-color-primary-light-5);
+      i {
+        color: var(--el-color-primary);
+      }
+    }
+  }
+  .icon-selector-active {
+    background-color: var(--el-color-primary-light-9);
+    border: 1px solid var(--el-color-primary-light-5);
+    i {
+      color: var(--el-color-primary);
+    }
+  }
+}
+</style>

+ 96 - 0
src/components/my-date-range/index.vue

@@ -0,0 +1,96 @@
+<template>
+  <el-date-picker
+    v-model="dateRange"
+    format="YYYY-MM-DD"
+    type="daterange"
+    start-placeholder="开始时间"
+    end-placeholder="结束时间"
+    :clearable="false"
+    :shortcuts="shortcuts"
+    @change="change"
+  ></el-date-picker>
+</template>
+
+<script lang="ts">
+import dayjs from 'dayjs'
+import { defineComponent, reactive, toRefs } from 'vue'
+
+export default defineComponent({
+  name: 'MyDateRange',
+  setup() {
+    const state = reactive({
+      dateRange: [dayjs().subtract(1, 'months').startOf('day'), dayjs().endOf('day')],
+      shortcuts: [
+        {
+          text: '最近一年',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().subtract(1, 'years').startOf('day')
+            return [start, end]
+          },
+        },
+        {
+          text: '最近半年',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().subtract(6, 'months').startOf('day')
+            return [start, end]
+          },
+        },
+        {
+          text: '最近三月',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().subtract(3, 'months').startOf('day')
+            return [start, end]
+          },
+        },
+        {
+          text: '最近一月',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().subtract(1, 'months').startOf('day')
+            return [start, end]
+          },
+        },
+        {
+          text: '最近七天',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().subtract(7, 'days').startOf('day')
+            return [start, end]
+          },
+        },
+        {
+          text: '最近三天',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().subtract(3, 'days').startOf('day')
+            return [start, end]
+          },
+        },
+        {
+          text: '今天',
+          value: () => {
+            const end = dayjs().endOf('day')
+            const start = dayjs().startOf('day')
+            return [start, end]
+          },
+        },
+      ],
+    })
+
+    const change = (value: any) => {
+      const end = dayjs(value[1]).endOf('day')
+      const start = dayjs(value[0]).startOf('day')
+      state.dateRange[0] = start
+      state.dateRange[1] = end
+    }
+
+    return {
+      change,
+      ...toRefs(state),
+    }
+  },
+})
+</script>

+ 48 - 0
src/components/my-dropdown-more/index.vue

@@ -0,0 +1,48 @@
+<template>
+  <el-dropdown style="margin-left: 10px" v-bind="$attrs">
+    <el-button size="small" text type="primary"
+      >{{ innerButtonText }}<el-icon class="el-icon--right"><component :is="innerIcon" /></el-icon
+    ></el-button>
+    <template #dropdown>
+      <slot name="dropdown" />
+    </template>
+  </el-dropdown>
+</template>
+
+<script lang="ts">
+import { defineComponent, computed, PropType } from 'vue'
+
+export default defineComponent({
+  name: 'MyDropdownMore',
+  props: {
+    iconOnly: {
+      type: Boolean,
+      default: false,
+    },
+    icon: {
+      type: String as PropType<string | undefined | null>,
+      default: '',
+    },
+    buttonText: {
+      type: String as PropType<string | undefined | null>,
+      default: '',
+    },
+  },
+  setup(props) {
+    const innerIcon = computed(() => {
+      return props.icon ? props.icon : props.iconOnly ? 'ele-MoreFilled' : 'ele-ArrowDown'
+    })
+
+    const innerButtonText = computed(() => {
+      return props.iconOnly ? '' : props.buttonText ? props.buttonText : '更多'
+    })
+
+    return {
+      innerIcon,
+      innerButtonText,
+    }
+  },
+})
+</script>
+
+<style scoped lang="scss"></style>

+ 251 - 0
src/components/my-select-icon/icon-select.vue

@@ -0,0 +1,251 @@
+<template>
+  <el-dialog v-model="state.showDialog" destroy-on-close :title="title" append-to-body draggable width="780px">
+    <div style="padding: 0px 0px 8px 8px; background-color: var(--ba-bg-color)">
+      <div>
+        <el-input v-model="state.fontIconSearch" placeholder="筛选图标" clearable />
+      </div>
+      <div class="icon-selector-popper">
+        <div class="icon-selector-warp">
+          <div class="icon-selector-warp-title flex">
+            <div class="flex-auto">{{ title }}</div>
+            <div class="icon-selector-warp-title-tab" v-if="type === 'all'">
+              <span :class="{ 'span-active': state.fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标">
+                ali
+              </span>
+              <span :class="{ 'span-active': state.fontIconType === 'ele' }" @click="onIconChange('ele')" class="ml10" title="elementPlus 图标">
+                ele
+              </span>
+              <span :class="{ 'span-active': state.fontIconType === 'awe' }" @click="onIconChange('awe')" class="ml10" title="fontawesome 图标">
+                awe
+              </span>
+            </div>
+          </div>
+          <div class="icon-selector-warp-row">
+            <el-row v-if="fontIconSheetsFilterList.length > 0">
+              <el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" @click="onColClick(v)" v-for="(v, k) in fontIconSheetsFilterList" :key="k">
+                <div class="icon-selector-warp-item" :class="{ 'icon-selector-active': state.fontIconPrefix === v }" @dblclick="onSure">
+                  <div class="flex-margin">
+                    <div class="icon-selector-warp-item-value my-flex-column my-flex-items-center">
+                      <SvgIcon :name="v" />
+                      <div class="icon-name my-line-1" :title="v">{{ v }}</div>
+                    </div>
+                  </div>
+                </div>
+              </el-col>
+            </el-row>
+            <el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
+          </div>
+        </div>
+      </div>
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="onCancel" size="default">取 消</el-button>
+        <el-button type="primary" @click="onSure" size="default" :loading="sureLoading">确 定</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { reactive, onMounted, computed, PropType } from 'vue'
+import initIconfont from '/@/utils/getStyleSheets'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  title: {
+    type: String,
+    default: '',
+  },
+  sureLoading: {
+    type: Boolean,
+    default: false,
+  },
+  // icon 图标类型
+  type: {
+    type: String,
+    default: () => 'all',
+  },
+  // 自定义空状态描述文字
+  emptyDescription: {
+    type: String,
+    default: () => '无相关图标',
+  },
+  modelValue: {
+    type: String as PropType<string | undefined | null>,
+    default: () => '',
+  },
+})
+
+// 定义子组件向父组件传值/事件
+const emits = defineEmits(['update:modelValue', 'get', 'clear', 'sure'])
+
+const state = reactive({
+  showDialog: false,
+  loading: false,
+  fontIconPrefix: props.modelValue,
+  fontIconSearch: '',
+  fontIconSheetsList: [],
+  fontIconType: 'ele',
+})
+
+// 处理 icon type 类型为 all 时,类型 ali、ele、awe 回显问题
+const initFontIconTypeEcho = () => {
+  if (props.modelValue!.indexOf('iconfont') > -1) onIconChange('ali')
+  else if (props.modelValue!.indexOf('ele-') > -1) onIconChange('ele')
+  else if (props.modelValue!.indexOf('fa') > -1) onIconChange('awe')
+  else onIconChange('ele')
+}
+
+// 图标搜索及图标数据显示
+const fontIconSheetsFilterList = computed(() => {
+  if (!state.fontIconSearch) return state.fontIconSheetsList
+  let search = state.fontIconSearch.trim().toLowerCase()
+  return state.fontIconSheetsList.filter((item: string) => {
+    if (item.toLowerCase()?.indexOf(search) !== -1) return item
+  })
+})
+
+// 初始化数据
+const initFontIconData = async (type: string) => {
+  state.fontIconSheetsList = []
+  if (type === 'ali') {
+    await initIconfont.ali().then((res: any) => {
+      // 阿里字体图标使用 `iconfont xxx`
+      state.fontIconSheetsList = res.map((i: string) => `iconfont ${i}`)
+    })
+  } else if (type === 'ele') {
+    await initIconfont.ele().then((res: any) => {
+      state.fontIconSheetsList = res
+    })
+  } else if (type === 'awe') {
+    await initIconfont.awe().then((res: any) => {
+      // fontawesome字体图标使用 `fa xxx`
+      state.fontIconSheetsList = res.map((i: string) => `fa ${i}`)
+    })
+  }
+}
+
+// 图标点击切换
+const onIconChange = (type: string) => {
+  state.fontIconType = type
+  initFontIconData(type)
+}
+
+// 获取当前点击的 icon 图标
+const onColClick = (v: string) => {
+  state.fontIconPrefix = v
+  emits('get', state.fontIconPrefix)
+  emits('update:modelValue', state.fontIconPrefix)
+}
+
+// 页面加载时
+onMounted(() => {})
+
+// 打开对话框
+const open = async () => {
+  await initFontIconTypeEcho()
+  state.showDialog = true
+}
+
+// 关闭对话框
+const close = () => {
+  state.showDialog = false
+}
+// 取消
+const onCancel = () => {
+  state.showDialog = false
+}
+
+// 确定
+const onSure = () => {
+  emits('sure', state.fontIconPrefix)
+}
+
+defineExpose({
+  open,
+  close,
+})
+</script>
+
+<script lang="ts">
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'my-icon-select',
+})
+</script>
+
+<style scoped lang="scss">
+:deep(.el-dialog__body) {
+  padding: 5px 10px;
+}
+.icon-selector-popper {
+  padding: 0 !important;
+  .icon-selector-warp {
+    .icon-selector-warp-title {
+      height: 40px;
+      line-height: 40px;
+      padding: 0px 5px;
+      .icon-selector-warp-title-tab {
+        span {
+          cursor: pointer;
+          &:hover {
+            color: var(--el-color-primary);
+            text-decoration: underline;
+          }
+        }
+        .span-active {
+          color: var(--el-color-primary);
+          text-decoration: underline;
+        }
+      }
+    }
+    .icon-selector-warp-row {
+      border: 1px solid var(--el-border-color);
+      border-right-width: 0px;
+      border-bottom-width: 0px;
+      border-top: 1px solid var(--el-border-color);
+      .el-scrollbar__bar.is-horizontal {
+        display: none;
+      }
+      .icon-selector-warp-item {
+        display: flex;
+        border: 1px solid var(--el-border-color);
+        height: 90px;
+        border-left-width: 0px;
+        border-top-width: 0px;
+        .icon-selector-warp-item-value {
+          i {
+            font-size: 20px !important;
+            color: var(--el-text-color-regular);
+          }
+          .icon-name {
+            margin-top: 8px;
+            padding: 0px 5px;
+          }
+        }
+        &:hover {
+          cursor: pointer;
+          background-color: var(--el-color-primary-light-9);
+          .icon-selector-warp-item-value {
+            i,
+            .icon-name {
+              color: var(--el-color-primary);
+            }
+          }
+        }
+      }
+      .icon-selector-active {
+        background-color: var(--el-color-primary-light-9);
+        .icon-selector-warp-item-value {
+          i,
+          .icon-name {
+            color: var(--el-color-primary);
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 64 - 0
src/components/my-select-icon/index.vue

@@ -0,0 +1,64 @@
+<template>
+  <el-input v-model="state.value" placeholder="请选择图标" class="w100" @clear="onClear" v-bind="$attrs">
+    <template #prepend>
+      <SvgIcon :name="state.value" />
+    </template>
+    <template #append>
+      <el-button icon="ele-MoreFilled" @click="onOpen" />
+    </template>
+  </el-input>
+
+  <icon-select ref="iconSelectRef" title="选择图标" :modal="true" @sure="onSure" v-model="state.value" v-bind="$attrs"></icon-select>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, PropType, watch } from 'vue'
+import iconSelect from './icon-select.vue'
+
+// const iconSelect = defineAsyncComponent(() => import('./icon-select.vue'))
+
+const props = defineProps({
+  modelValue: String as PropType<string | undefined | null>,
+})
+
+const emits = defineEmits(['update:modelValue'])
+
+const iconSelectRef = ref()
+
+const state = reactive({
+  value: props.modelValue,
+})
+
+const onOpen = () => {
+  iconSelectRef.value!.open()
+}
+
+const onClear = () => {
+  emits('update:modelValue', undefined)
+}
+
+const onSure = async (value: string) => {
+  iconSelectRef.value.close()
+  if (value) {
+    state.value = value
+    emits('update:modelValue', value)
+  }
+}
+
+watch(
+  () => state.value,
+  () => {
+    emits('update:modelValue', state.value)
+  }
+)
+</script>
+
+<script lang="ts">
+import { defineComponent } from 'vue'
+
+export default defineComponent({
+  name: 'my-select-icon',
+})
+</script>
+
+<style scoped lang="scss"></style>

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

@@ -0,0 +1,191 @@
+<template>
+  <div class="notice-bar" :style="{ background, height: `${height}px` }" v-show="!state.isMode">
+    <div class="notice-bar-warp" :style="{ color, fontSize: `${size}px` }">
+      <i v-if="leftIcon" class="notice-bar-warp-left-icon" :class="leftIcon"></i>
+      <div class="notice-bar-warp-text-box" ref="noticeBarWarpRef">
+        <div class="notice-bar-warp-text" ref="noticeBarTextRef" v-if="!scrollable">{{ text }}</div>
+        <div class="notice-bar-warp-slot" v-else><slot /></div>
+      </div>
+      <SvgIcon :name="rightIcon" v-if="rightIcon" class="notice-bar-warp-right-icon" @click="onRightIconClick" />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="noticeBar">
+import { reactive, ref, onMounted, nextTick } from 'vue'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  // 通知栏模式,可选值为 closeable link
+  mode: {
+    type: String,
+    default: () => '',
+  },
+  // 通知文本内容
+  text: {
+    type: String,
+    default: () => '',
+  },
+  // 通知文本颜色
+  color: {
+    type: String,
+    default: () => 'var(--el-color-warning)',
+  },
+  // 通知背景色
+  background: {
+    type: String,
+    default: () => 'var(--el-color-warning-light-9)',
+  },
+  // 字体大小,单位px
+  size: {
+    type: [Number, String],
+    default: () => 14,
+  },
+  // 通知栏高度,单位px
+  height: {
+    type: Number,
+    default: () => 40,
+  },
+  // 动画延迟时间 (s)
+  delay: {
+    type: Number,
+    default: () => 1,
+  },
+  // 滚动速率 (px/s)
+  speed: {
+    type: Number,
+    default: () => 100,
+  },
+  // 是否开启垂直滚动
+  scrollable: {
+    type: Boolean,
+    default: () => false,
+  },
+  // 自定义左侧图标
+  leftIcon: {
+    type: String,
+    default: () => '',
+  },
+  // 自定义右侧图标
+  rightIcon: {
+    type: String,
+    default: () => '',
+  },
+})
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['close', 'link'])
+
+// 定义变量内容
+const noticeBarWarpRef = ref()
+const noticeBarTextRef = ref()
+const state = reactive({
+  order: 1,
+  oneTime: 0,
+  twoTime: 0,
+  warpOWidth: 0,
+  textOWidth: 0,
+  isMode: false,
+})
+
+// 初始化 animation 各项参数
+const initAnimation = () => {
+  nextTick(() => {
+    state.warpOWidth = noticeBarWarpRef.value.offsetWidth
+    state.textOWidth = noticeBarTextRef.value.offsetWidth
+    document.styleSheets[0].insertRule(`@keyframes oneAnimation {0% {left: 0px;} 100% {left: -${state.textOWidth}px;}}`)
+    document.styleSheets[0].insertRule(`@keyframes twoAnimation {0% {left: ${state.warpOWidth}px;} 100% {left: -${state.textOWidth}px;}}`)
+    computeAnimationTime()
+    setTimeout(() => {
+      changeAnimation()
+    }, props.delay * 1000)
+  })
+}
+// 计算 animation 滚动时长
+const computeAnimationTime = () => {
+  state.oneTime = state.textOWidth / props.speed
+  state.twoTime = (state.textOWidth + state.warpOWidth) / props.speed
+}
+// 改变 animation 动画调用
+const changeAnimation = () => {
+  if (state.order === 1) {
+    noticeBarTextRef.value.style.cssText = `animation: oneAnimation ${state.oneTime}s linear; opactity: 1;}`
+    state.order = 2
+  } else {
+    noticeBarTextRef.value.style.cssText = `animation: twoAnimation ${state.twoTime}s linear infinite; opacity: 1;`
+  }
+}
+// 监听 animation 动画的结束
+const listenerAnimationend = () => {
+  noticeBarTextRef.value.addEventListener(
+    'animationend',
+    () => {
+      changeAnimation()
+    },
+    false
+  )
+}
+// 右侧 icon 图标点击
+const onRightIconClick = () => {
+  if (!props.mode) return false
+  if (props.mode === 'closeable') {
+    state.isMode = true
+    emit('close')
+  } else if (props.mode === 'link') {
+    emit('link')
+  }
+}
+// 页面加载时
+onMounted(() => {
+  if (props.scrollable) return false
+  initAnimation()
+  listenerAnimationend()
+})
+</script>
+
+<style scoped lang="scss">
+.notice-bar {
+  padding: 0 15px;
+  width: 100%;
+  border-radius: 4px;
+  .notice-bar-warp {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    height: inherit;
+    .notice-bar-warp-text-box {
+      flex: 1;
+      height: inherit;
+      display: flex;
+      align-items: center;
+      overflow: hidden;
+      position: relative;
+      .notice-bar-warp-text {
+        white-space: nowrap;
+        position: absolute;
+        left: 0;
+      }
+      .notice-bar-warp-slot {
+        width: 100%;
+        white-space: nowrap;
+        :deep(.el-carousel__item) {
+          display: flex;
+          align-items: center;
+        }
+      }
+    }
+    .notice-bar-warp-left-icon {
+      width: 24px;
+      font-size: inherit !important;
+    }
+    .notice-bar-warp-right-icon {
+      width: 24px;
+      text-align: right;
+      font-size: inherit !important;
+      &:hover {
+        cursor: pointer;
+      }
+    }
+  }
+}
+</style>

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

@@ -0,0 +1,63 @@
+<template>
+  <i v-if="isShowIconSvg" class="el-icon" :style="setIconSvgStyle">
+    <component :is="getIconName" />
+  </i>
+  <div v-else-if="isShowIconImg" :style="setIconImgOutStyle">
+    <img :src="getIconName" :style="setIconSvgInsStyle" />
+  </div>
+  <i v-else :class="getIconName" :style="setIconSvgStyle" />
+</template>
+
+<script setup lang="ts" name="svgIcon">
+import { computed } from 'vue'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  // svg 图标组件名字
+  name: {
+    type: String,
+  },
+  // svg 大小
+  size: {
+    type: Number,
+    default: () => 14,
+  },
+  // svg 颜色
+  color: {
+    type: String,
+  },
+})
+
+// 在线链接、本地引入地址前缀
+// https://gitee.com/lyt-top/vue-next-admin/issues/I62OVL
+const linesString = ['https', 'http', '/src', '/assets', 'data:image', import.meta.env.VITE_PUBLIC_PATH]
+
+// 获取 icon 图标名称
+const getIconName = computed(() => {
+  return props?.name
+})
+// 用于判断 element plus 自带 svg 图标的显示、隐藏
+const isShowIconSvg = computed(() => {
+  return props?.name?.startsWith('ele-')
+})
+// 用于判断在线链接、本地引入等图标显示、隐藏
+const isShowIconImg = computed(() => {
+  return linesString.find((str) => props.name?.startsWith(str))
+})
+// 设置图标样式
+const setIconSvgStyle = computed(() => {
+  return `font-size: ${props.size}px;color: ${props.color};`
+})
+// 设置图片样式
+const setIconImgOutStyle = computed(() => {
+  return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`
+})
+// 设置图片样式
+// https://gitee.com/lyt-top/vue-next-admin/issues/I59ND0
+const setIconSvgInsStyle = computed(() => {
+  const filterStyle: string[] = []
+  const compatibles: string[] = ['-webkit', '-ms', '-o', '-moz']
+  compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`))
+  return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`
+})
+</script>

+ 256 - 0
src/components/table/index.vue

@@ -0,0 +1,256 @@
+<template>
+  <div class="table-container">
+    <el-table
+      :data="data"
+      :border="setBorder"
+      v-bind="$attrs"
+      row-key="id"
+      stripe
+      style="width: 100%"
+      v-loading="config.loading"
+      @selection-change="onSelectionChange"
+    >
+      <el-table-column type="selection" :reserve-selection="true" width="30" v-if="config.isSelection" />
+      <el-table-column type="index" label="序号" width="60" v-if="config.isSerialNo" />
+      <el-table-column
+        v-for="(item, index) in setHeader"
+        :key="index"
+        show-overflow-tooltip
+        :prop="item.key"
+        :width="item.colWidth"
+        :label="item.title"
+      >
+        <template v-slot="scope">
+          <template v-if="item.type === 'image'">
+            <img :src="scope.row[item.key]" :width="item.width" :height="item.height" />
+          </template>
+          <template v-else>
+            {{ scope.row[item.key] }}
+          </template>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="100" v-if="config.isOperate">
+        <template v-slot="scope">
+          <el-popconfirm title="确定删除吗?" @confirm="onDelRow(scope.row)">
+            <template #reference>
+              <el-button text type="primary">删除</el-button>
+            </template>
+          </el-popconfirm>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <el-empty description="暂无数据" />
+      </template>
+    </el-table>
+    <div class="table-footer mt15">
+      <el-pagination
+        v-model:current-page="state.page.pageNum"
+        v-model:page-size="state.page.pageSize"
+        :pager-count="5"
+        :page-sizes="[10, 20, 30]"
+        :total="config.total"
+        layout="total, sizes, prev, pager, next, jumper"
+        background
+        @size-change="onHandleSizeChange"
+        @current-change="onHandleCurrentChange"
+      >
+      </el-pagination>
+      <div class="table-footer-tool">
+        <SvgIcon name="iconfont icon-yunxiazai_o" :size="22" title="导出" @click="onImportTable" />
+        <SvgIcon name="iconfont icon-shuaxin" :size="22" title="刷新" @click="onRefreshTable" />
+        <el-popover
+          placement="top-end"
+          trigger="click"
+          transition="el-zoom-in-top"
+          popper-class="table-tool-popper"
+          :width="300"
+          :persistent="false"
+          @show="onSetTable"
+        >
+          <template #reference>
+            <SvgIcon name="iconfont icon-quanjushezhi_o" :size="22" title="设置" />
+          </template>
+          <template #default>
+            <div class="tool-box">
+              <el-tooltip content="拖动进行排序" placement="top-start">
+                <SvgIcon name="fa fa-question-circle-o" :size="17" class="ml11" color="#909399" />
+              </el-tooltip>
+              <el-checkbox
+                v-model="state.checkListAll"
+                :indeterminate="state.checkListIndeterminate"
+                class="ml10 mr1"
+                label="列显示"
+                @change="onCheckAllChange"
+              />
+              <el-checkbox v-model="getConfig.isSerialNo" class="ml12 mr1" label="序号" />
+              <el-checkbox v-model="getConfig.isSelection" class="ml12 mr1" label="多选" />
+            </div>
+            <el-scrollbar>
+              <div ref="toolSetRef" class="tool-sortable">
+                <div class="tool-sortable-item" v-for="v in header" :key="v.key" :data-key="v.key">
+                  <i class="fa fa-arrows-alt handle cursor-pointer"></i>
+                  <el-checkbox v-model="v.isCheck" size="default" class="ml12 mr8" :label="v.title" @change="onCheckChange" />
+                </div>
+              </div>
+            </el-scrollbar>
+          </template>
+        </el-popover>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="netxTable">
+import { reactive, computed, nextTick, ref } from 'vue'
+import { ElMessage } from 'element-plus'
+import table2excel from 'js-table2excel'
+import Sortable from 'sortablejs'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import '/@/theme/tableTool.scss'
+
+// 定义父组件传过来的值
+const props = defineProps({
+  // 列表内容
+  data: {
+    type: Array<EmptyObjectType>,
+    default: () => [],
+  },
+  // 表头内容
+  header: {
+    type: Array<EmptyObjectType>,
+    default: () => [],
+  },
+  // 配置项
+  config: {
+    type: Object,
+    default: () => {},
+  },
+})
+
+// 定义子组件向父组件传值/事件
+const emit = defineEmits(['delRow', 'pageChange', 'sortHeader'])
+
+// 定义变量内容
+const toolSetRef = ref()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const state = reactive({
+  page: {
+    pageNum: 1,
+    pageSize: 10,
+  },
+  selectlist: [] as EmptyObjectType[],
+  checkListAll: true,
+  checkListIndeterminate: false,
+})
+
+// 设置边框显示/隐藏
+const setBorder = computed(() => {
+  return props.config.isBorder ? true : false
+})
+// 获取父组件 配置项(必传)
+const getConfig = computed(() => {
+  return props.config
+})
+// 设置 tool header 数据
+const setHeader = computed(() => {
+  return props.header.filter((v) => v.isCheck)
+})
+// tool 列显示全选改变时
+const onCheckAllChange = <T>(val: T) => {
+  if (val) props.header.forEach((v) => (v.isCheck = true))
+  else props.header.forEach((v) => (v.isCheck = false))
+  state.checkListIndeterminate = false
+}
+// tool 列显示当前项改变时
+const onCheckChange = () => {
+  const headers = props.header.filter((v) => v.isCheck).length
+  state.checkListAll = headers === props.header.length
+  state.checkListIndeterminate = headers > 0 && headers < props.header.length
+}
+// 表格多选改变时,用于导出
+const onSelectionChange = (val: EmptyObjectType[]) => {
+  state.selectlist = val
+}
+// 删除当前项
+const onDelRow = (row: EmptyObjectType) => {
+  emit('delRow', row)
+}
+// 分页改变
+const onHandleSizeChange = (val: number) => {
+  state.page.pageSize = val
+  emit('pageChange', state.page)
+}
+// 分页改变
+const onHandleCurrentChange = (val: number) => {
+  state.page.pageNum = val
+  emit('pageChange', state.page)
+}
+// 搜索时,分页还原成默认
+const pageReset = () => {
+  state.page.pageNum = 1
+  state.page.pageSize = 10
+  emit('pageChange', state.page)
+}
+// 导出
+const onImportTable = () => {
+  if (state.selectlist.length <= 0) return ElMessage.warning('请先选择要导出的数据')
+  table2excel(props.header, state.selectlist, `${themeConfig.value.globalTitle} ${new Date().toLocaleString()}`)
+}
+// 刷新
+const onRefreshTable = () => {
+  emit('pageChange', state.page)
+}
+// 设置
+const onSetTable = () => {
+  nextTick(() => {
+    const sortable = Sortable.create(toolSetRef.value, {
+      handle: '.handle',
+      dataIdAttr: 'data-key',
+      animation: 150,
+      onEnd: () => {
+        const headerList: EmptyObjectType[] = []
+        sortable.toArray().forEach((val) => {
+          props.header.forEach((v) => {
+            if (v.key === val) headerList.push({ ...v })
+          })
+        })
+        emit('sortHeader', headerList)
+      },
+    })
+  })
+}
+
+// 暴露变量
+defineExpose({
+  pageReset,
+})
+</script>
+
+<style scoped lang="scss">
+.table-container {
+  display: flex;
+  flex-direction: column;
+  .el-table {
+    flex: 1;
+  }
+  .table-footer {
+    display: flex;
+    .table-footer-tool {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      i {
+        margin-right: 10px;
+        cursor: pointer;
+        color: var(--el-text-color-regular);
+        &:last-of-type {
+          margin-right: 0;
+        }
+      }
+    }
+  }
+}
+</style>

+ 40 - 0
src/directive/authDirective.ts

@@ -0,0 +1,40 @@
+import type { App } from 'vue'
+import { useUserInfo } from '/@/stores/userInfo'
+import { judementSameArr } from '/@/utils/arrayOperation'
+
+/**
+ * 用户权限指令
+ * @directive 单个权限验证(v-auth="xxx")
+ * @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
+ * @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
+ */
+export function authDirective(app: App) {
+  // 单个权限验证(v-auth="xxx")
+  app.directive('auth', {
+    mounted(el, binding) {
+      const stores = useUserInfo()
+      if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el)
+    },
+  })
+  // 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
+  app.directive('auths', {
+    mounted(el, binding) {
+      let flag = false
+      const stores = useUserInfo()
+      stores.userInfos.authBtnList.map((val: string) => {
+        binding.value.map((v: string) => {
+          if (val === v) flag = true
+        })
+      })
+      if (!flag) el.parentNode.removeChild(el)
+    },
+  })
+  // 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
+  app.directive('auth-all', {
+    mounted(el, binding) {
+      const stores = useUserInfo()
+      const flag = judementSameArr(binding.value, stores.userInfos.authBtnList)
+      if (!flag) el.parentNode.removeChild(el)
+    },
+  })
+}

+ 178 - 0
src/directive/customDirective.ts

@@ -0,0 +1,178 @@
+import type { App } from 'vue'
+
+/**
+ * 按钮波浪指令
+ * @directive 默认方式:v-waves,如 `<div v-waves></div>`
+ * @directive 参数方式:v-waves=" |light|red|orange|purple|green|teal",如 `<div v-waves="'light'"></div>`
+ */
+export function wavesDirective(app: App) {
+  app.directive('waves', {
+    mounted(el, binding) {
+      el.classList.add('waves-effect')
+      binding.value && el.classList.add(`waves-${binding.value}`)
+      function setConvertStyle(obj: { [key: string]: unknown }) {
+        let style: string = ''
+        for (let i in obj) {
+          if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`
+        }
+        return style
+      }
+      function onCurrentClick(e: { [key: string]: unknown }) {
+        let elDiv = document.createElement('div')
+        elDiv.classList.add('waves-ripple')
+        el.appendChild(elDiv)
+        let styles = {
+          left: `${e.layerX}px`,
+          top: `${e.layerY}px`,
+          opacity: 1,
+          transform: `scale(${(el.clientWidth / 100) * 10})`,
+          'transition-duration': `750ms`,
+          'transition-timing-function': `cubic-bezier(0.250, 0.460, 0.450, 0.940)`,
+        }
+        elDiv.setAttribute('style', setConvertStyle(styles))
+        setTimeout(() => {
+          elDiv.setAttribute(
+            'style',
+            setConvertStyle({
+              opacity: 0,
+              transform: styles.transform,
+              left: styles.left,
+              top: styles.top,
+            })
+          )
+          setTimeout(() => {
+            elDiv && el.removeChild(elDiv)
+          }, 750)
+        }, 450)
+      }
+      el.addEventListener('mousedown', onCurrentClick, false)
+    },
+    unmounted(el) {
+      el.addEventListener('mousedown', () => {})
+    },
+  })
+}
+
+/**
+ * 自定义拖动指令
+ * @description  使用方式:v-drag="[dragDom,dragHeader]",如 `<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']"></div>`
+ * @description dragDom 要拖动的元素,dragHeader 要拖动的 Header 位置
+ * @link 注意:https://github.com/element-plus/element-plus/issues/522
+ * @lick 参考:https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242
+ */
+export function dragDirective(app: App) {
+  app.directive('drag', {
+    mounted(el, binding) {
+      if (!binding.value) return false
+
+      const dragDom = document.querySelector(binding.value[0]) as HTMLElement
+      const dragHeader = document.querySelector(binding.value[1]) as HTMLElement
+
+      dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`)
+
+      function down(e: any, type: string) {
+        // 鼠标按下,计算当前元素距离可视区的距离
+        const disX = type === 'pc' ? e.clientX - dragHeader.offsetLeft : e.touches[0].clientX - dragHeader.offsetLeft
+        const disY = type === 'pc' ? e.clientY - dragHeader.offsetTop : e.touches[0].clientY - dragHeader.offsetTop
+
+        // body当前宽度
+        const screenWidth = document.body.clientWidth
+        // 可见区域高度(应为body高度,可某些环境下无法获取)
+        const screenHeight = document.documentElement.clientHeight
+
+        // 对话框宽度
+        const dragDomWidth = dragDom.offsetWidth
+        // 对话框高度
+        const dragDomheight = dragDom.offsetHeight
+
+        const minDragDomLeft = dragDom.offsetLeft
+        const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
+
+        const minDragDomTop = dragDom.offsetTop
+        const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
+
+        // 获取到的值带px 正则匹配替换
+        let styL: any = getComputedStyle(dragDom).left
+        let styT: any = getComputedStyle(dragDom).top
+
+        // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+        if (styL.includes('%')) {
+          styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
+          styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
+        } else {
+          styL = +styL.replace(/\px/g, '')
+          styT = +styT.replace(/\px/g, '')
+        }
+
+        return {
+          disX,
+          disY,
+          minDragDomLeft,
+          maxDragDomLeft,
+          minDragDomTop,
+          maxDragDomTop,
+          styL,
+          styT,
+        }
+      }
+
+      function move(e: any, type: string, obj: any) {
+        let { disX, disY, minDragDomLeft, maxDragDomLeft, minDragDomTop, maxDragDomTop, styL, styT } = obj
+
+        // 通过事件委托,计算移动的距离
+        let left = type === 'pc' ? e.clientX - disX : e.touches[0].clientX - disX
+        let top = type === 'pc' ? e.clientY - disY : e.touches[0].clientY - disY
+
+        // 边界处理
+        if (-left > minDragDomLeft) {
+          left = -minDragDomLeft
+        } else if (left > maxDragDomLeft) {
+          left = maxDragDomLeft
+        }
+
+        if (-top > minDragDomTop) {
+          top = -minDragDomTop
+        } else if (top > maxDragDomTop) {
+          top = maxDragDomTop
+        }
+
+        // 移动当前元素
+        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
+      }
+
+      /**
+       * pc端
+       * onmousedown 鼠标按下触发事件
+       * onmousemove 鼠标按下时持续触发事件
+       * onmouseup 鼠标抬起触发事件
+       */
+      dragHeader.onmousedown = (e) => {
+        const obj = down(e, 'pc')
+        document.onmousemove = (e) => {
+          move(e, 'pc', obj)
+        }
+        document.onmouseup = () => {
+          document.onmousemove = null
+          document.onmouseup = null
+        }
+      }
+
+      /**
+       * 移动端
+       * ontouchstart 当按下手指时,触发ontouchstart
+       * ontouchmove 当移动手指时,触发ontouchmove
+       * ontouchend 当移走手指时,触发ontouchend
+       */
+      dragHeader.ontouchstart = (e) => {
+        const obj = down(e, 'app')
+        document.ontouchmove = (e) => {
+          move(e, 'app', obj)
+        }
+        document.ontouchend = () => {
+          document.ontouchmove = null
+          document.ontouchend = null
+        }
+      }
+    },
+  })
+}

+ 18 - 0
src/directive/index.ts

@@ -0,0 +1,18 @@
+import type { App } from 'vue'
+import { authDirective } from '/@/directive/authDirective'
+import { wavesDirective, dragDirective } from '/@/directive/customDirective'
+
+/**
+ * 导出指令方法:v-xxx
+ * @methods authDirective 用户权限指令,用法:v-auth
+ * @methods wavesDirective 按钮波浪指令,用法:v-waves
+ * @methods dragDirective 自定义拖动指令,用法:v-drag
+ */
+export function directive(app: App) {
+  // 用户权限指令
+  authDirective(app)
+  // 按钮波浪指令
+  wavesDirective(app)
+  // 自定义拖动指令
+  dragDirective(app)
+}

+ 9 - 0
src/globalProperties/index.ts

@@ -0,0 +1,9 @@
+import modal from './modal'
+import mitt from 'mitt'
+
+export default function installGlobalProperties(app: any) {
+  app.config.globalProperties.eventBus = mitt()
+
+  // 模态框对象
+  app.config.globalProperties.$modal = modal
+}

+ 95 - 0
src/globalProperties/modal.ts

@@ -0,0 +1,95 @@
+import { markRaw } from 'vue'
+import { ElMessage, ElMessageBox, ElNotification, ElLoading, ElMessageBoxOptions } from 'element-plus'
+import { Delete } from '@element-plus/icons-vue'
+import { i18n } from '/@/i18n/index'
+
+let loadingInstance: any
+
+export default {
+  // 消息提示
+  msg(content: any) {
+    ElMessage.info(content)
+  },
+  // 错误消息
+  msgError(content: any) {
+    ElMessage.error(content)
+  },
+  // 成功消息
+  msgSuccess(content: any) {
+    ElMessage.success(content)
+  },
+  // 警告消息
+  msgWarning(content: any) {
+    ElMessage.warning(content)
+  },
+  // 弹出提示
+  alert(content: any) {
+    ElMessageBox.alert(content, i18n.global.t('el.messagebox.title'))
+  },
+  // 错误提示
+  alertError(content: any) {
+    ElMessageBox.alert(content, i18n.global.t('el.messagebox.title'), { type: 'error' })
+  },
+  // 成功提示
+  alertSuccess(content: any) {
+    ElMessageBox.alert(content, i18n.global.t('el.messagebox.title'), { type: 'success' })
+  },
+  // 警告提示
+  alertWarning(content: any) {
+    ElMessageBox.alert(content, i18n.global.t('el.messagebox.title'), { type: 'warning' })
+  },
+  // 通知提示
+  notify(content: any) {
+    ElNotification.info(content)
+  },
+  // 错误通知
+  notifyError(content: any) {
+    ElNotification.error(content)
+  },
+  // 成功通知
+  notifySuccess(content: any) {
+    ElNotification.success(content)
+  },
+  // 警告通知
+  notifyWarning(content: any) {
+    ElNotification.warning(content)
+  },
+  // 确认窗体
+  confirm(content: any, elMessageBoxOptions: ElMessageBoxOptions) {
+    return ElMessageBox.confirm(content, i18n.global.t('el.messagebox.title'), {
+      confirmButtonText: i18n.global.t('el.messagebox.confirm'),
+      cancelButtonText: i18n.global.t('el.messagebox.cancel'),
+      type: 'warning',
+      ...elMessageBoxOptions,
+    })
+  },
+  // 确认删除窗体
+  confirmDelete(content: any, elMessageBoxOptions: ElMessageBoxOptions) {
+    return ElMessageBox.confirm(content, i18n.global.t('el.messagebox.title'), {
+      confirmButtonText: i18n.global.t('el.messagebox.confirm'),
+      cancelButtonText: i18n.global.t('el.messagebox.cancel'),
+      type: 'warning',
+      icon: markRaw(Delete),
+      ...elMessageBoxOptions,
+    })
+  },
+  // 提交内容
+  prompt(content: any, elMessageBoxOptions: ElMessageBoxOptions) {
+    return ElMessageBox.prompt(content, i18n.global.t('el.messagebox.title'), {
+      confirmButtonText: i18n.global.t('el.messagebox.confirm'),
+      cancelButtonText: i18n.global.t('el.messagebox.cancel'),
+      type: 'warning',
+      ...elMessageBoxOptions,
+    })
+  },
+  // 打开遮罩层
+  loading(content: any) {
+    loadingInstance = ElLoading.service({
+      text: content,
+    })
+  },
+  // 关闭遮罩层
+  closeLoading() {
+    loadingInstance.close()
+  },
+}

+ 68 - 0
src/i18n/index.ts

@@ -0,0 +1,68 @@
+import { createI18n } from 'vue-i18n'
+import pinia from '/@/stores/index'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+
+// 定义语言国际化内容
+
+/**
+ * 说明:
+ * 须在 pages 下新建文件夹(建议 `要国际化界面目录` 与 `i18n 目录` 相同,方便查找),
+ * 注意国际化定义的字段,不要与原有的定义字段相同。
+ * 1、/src/i18n/lang 下的 ts 为框架的国际化内容
+ * 2、/src/i18n/pages 下的 ts 为各界面的国际化内容
+ */
+
+// element plus 自带国际化
+import enLocale from 'element-plus/lib/locale/lang/en'
+import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn'
+import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw'
+
+// 定义变量内容
+const messages = {}
+const element = { en: enLocale, 'zh-cn': zhcnLocale, 'zh-tw': zhtwLocale }
+const itemize = { en: [], 'zh-cn': [], 'zh-tw': [] }
+const modules: Record<string, any> = import.meta.glob('./**/*.ts', { eager: true })
+
+// 对自动引入的 modules 进行分类 en、zh-cn、zh-tw
+// https://vitejs.cn/vite3-cn/guide/features.html#glob-import
+for (const path in modules) {
+  const key = path.match(/(\S+)\/(\S+).ts/)
+  if (itemize[key![2]]) itemize[key![2]].push(modules[path].default)
+  else itemize[key![2]] = modules[path]
+}
+
+// 合并数组对象(非标准数组对象,数组中对象的每项 key、value 都不同)
+function mergeArrObj<T>(list: T, key: string) {
+  let obj = {}
+  list[key].forEach((i: EmptyObjectType) => {
+    obj = Object.assign({}, obj, i)
+  })
+  return obj
+}
+
+// 处理最终格式
+for (const key in itemize) {
+  messages[key] = {
+    name: key,
+    el: element[key].el,
+    message: mergeArrObj(itemize, key),
+  }
+}
+
+// 读取 pinia 默认语言
+const stores = useThemeConfig(pinia)
+const { themeConfig } = storeToRefs(stores)
+
+// 导出语言国际化
+// https://vue-i18n.intlify.dev/guide/essentials/fallback.html#explicit-fallback-with-one-locale
+export const i18n = createI18n({
+  legacy: false,
+  silentTranslationWarn: true,
+  missingWarn: false,
+  silentFallbackWarn: true,
+  fallbackWarn: false,
+  locale: themeConfig.value.globalI18n,
+  fallbackLocale: zhcnLocale.name,
+  messages,
+})

+ 192 - 0
src/i18n/lang/en.ts

@@ -0,0 +1,192 @@
+// 定义内容
+export default {
+  router: {
+    home: 'home',
+    system: 'system',
+    systemMenu: 'systemMenu',
+    systemRole: 'systemRole',
+    systemUser: 'systemUser',
+    systemDept: 'systemDept',
+    systemDic: 'systemDic',
+    limits: 'limits',
+    limitsFrontEnd: 'FrontEnd',
+    limitsFrontEndPage: 'FrontEndPage',
+    limitsFrontEndBtn: 'FrontEndBtn',
+    limitsBackEnd: 'BackEnd',
+    limitsBackEndEndPage: 'BackEndEndPage',
+    menu: 'menu',
+    menu1: 'menu1',
+    menu11: 'menu11',
+    menu12: 'menu12',
+    menu121: 'menu121',
+    menu122: 'menu122',
+    menu13: 'menu13',
+    menu2: 'menu2',
+    funIndex: 'function',
+    funTagsView: 'funTagsView',
+    funCountup: 'countup',
+    funWangEditor: 'wangEditor',
+    funCropper: 'cropper',
+    funQrcode: 'qrcode',
+    funEchartsMap: 'EchartsMap',
+    funPrintJs: 'PrintJs',
+    funClipboard: 'Copy cut',
+    funGridLayout: 'Drag layout',
+    funSplitpanes: 'Pane splitter',
+    funDragVerify: 'Validator',
+    pagesIndex: 'pages',
+    pagesFiltering: 'Filtering',
+    pagesFilteringDetails: 'FilteringDetails',
+    pagesFilteringDetails1: 'FilteringDetails1',
+    pagesIocnfont: 'iconfont icon',
+    pagesElement: 'element icon',
+    pagesAwesome: 'awesome icon',
+    pagesFormAdapt: 'FormAdapt',
+    pagesTableRules: 'pagesTableRules',
+    pagesFormI18n: 'FormI18n',
+    pagesFormRules: 'Multi form validation',
+    pagesDynamicForm: 'Dynamic complex form',
+    pagesWorkflow: 'Workflow',
+    pagesListAdapt: 'ListAdapt',
+    pagesWaterfall: 'Waterfall',
+    pagesSteps: 'Steps',
+    pagesPreview: 'Large preview',
+    pagesWaves: 'Wave effect',
+    pagesTree: 'tree alter table',
+    pagesDrag: 'Drag command',
+    pagesLazyImg: 'Image lazy loading',
+    makeIndex: 'makeIndex',
+    makeSelector: 'Icon selector',
+    makeNoticeBar: 'notification bar',
+    makeSvgDemo: 'Svgicon demo',
+    makeTableDemo: 'table demo',
+    paramsIndex: 'Routing parameters',
+    paramsCommon: 'General routing',
+    paramsDynamic: 'Dynamic routing',
+    paramsCommonDetails: 'General routing details',
+    paramsDynamicDetails: 'Dynamic routing details',
+    chartIndex: 'chartIndex',
+    visualizingIndex: 'visualizingIndex',
+    visualizingLinkDemo1: 'visualizingLinkDemo1',
+    visualizingLinkDemo2: 'visualizingLinkDemo2',
+    personal: 'personal',
+    tools: 'tools',
+    layoutLinkView: 'LinkView',
+    layoutIframeViewOne: 'IframeViewOne',
+    layoutIframeViewTwo: 'IframeViewTwo',
+  },
+  staticRoutes: {
+    signIn: 'signIn',
+    notFound: 'notFound',
+    noPower: 'noPower',
+  },
+  user: {
+    title0: 'Component size',
+    title1: 'Language switching',
+    title2: 'Menu search',
+    title3: 'Layout configuration',
+    title4: 'news',
+    title5: 'Full screen on',
+    title6: 'Full screen off',
+    dropdownLarge: 'large',
+    dropdownDefault: 'default',
+    dropdownSmall: 'small',
+    dropdown1: 'home page',
+    dropdown2: 'Personal Center',
+    dropdown3: '404',
+    dropdown4: '401',
+    dropdown5: 'Log out',
+    dropdown6: 'Code warehouse',
+    searchPlaceholder: 'Menu search: support Chinese, routing path',
+    newTitle: 'notice',
+    newBtn: 'All read',
+    newGo: 'Go to the notification center',
+    newDesc: 'No notice',
+    logOutTitle: 'Tips',
+    logOutMessage: 'This operation will log out. Do you want to continue?',
+    logOutConfirm: 'determine',
+    logOutCancel: 'cancel',
+    logOutExit: 'Exiting',
+  },
+  tagsView: {
+    refresh: 'refresh',
+    close: 'close',
+    closeOther: 'closeOther',
+    closeAll: 'closeAll',
+    fullscreen: 'fullscreen',
+    closeFullscreen: 'closeFullscreen',
+  },
+  notFound: {
+    foundTitle: 'Wrong address input, please re-enter the address~',
+    foundMsg: 'You can check the web address first, and then re-enter or give us feedback.',
+    foundBtn: 'Back to home page',
+  },
+  noAccess: {
+    accessTitle: 'You are not authorized to operate~',
+    accessMsg: 'Contact information: add QQ group discussion 665452019',
+    accessBtn: 'Reauthorization',
+  },
+  layout: {
+    configTitle: 'Layout configuration',
+    oneTitle: 'Global Themes',
+    twoTopTitle: 'top bar set up',
+    twoMenuTitle: 'Menu set up',
+    twoColumnsTitle: 'Columns set up',
+    twoTopBar: 'Top bar background',
+    twoTopBarColor: 'Top bar default font color',
+    twoIsTopBarColorGradual: 'Top bar gradient',
+    twoMenuBar: 'Menu background',
+    twoMenuBarColor: 'Menu default font color',
+    twoMenuBarActiveColor: 'Menu Highlight Color',
+    twoIsMenuBarColorGradual: 'Menu gradient',
+    twoColumnsMenuBar: 'Column menu background',
+    twoColumnsMenuBarColor: 'Default font color bar menu',
+    twoIsColumnsMenuBarColorGradual: 'Column gradient',
+    twoIsColumnsMenuHoverPreload: 'Column Menu Hover Preload',
+    threeTitle: 'Interface settings',
+    threeIsCollapse: 'Menu horizontal collapse',
+    threeIsUniqueOpened: 'Menu accordion',
+    threeIsFixedHeader: 'Fixed header',
+    threeIsClassicSplitMenu: 'Classic layout split menu',
+    threeIsLockScreen: 'Open the lock screen',
+    threeLockScreenTime: 'screen locking(s/s)',
+    fourTitle: 'Interface display',
+    fourIsShowLogo: 'Sidebar logo',
+    fourIsBreadcrumb: 'Open breadcrumb',
+    fourIsBreadcrumbIcon: 'Open breadcrumb icon',
+    fourIsTagsview: 'Open tagsview',
+    fourIsTagsviewIcon: 'Open tagsview Icon',
+    fourIsCacheTagsView: 'Enable tagsview cache',
+    fourIsSortableTagsView: 'Enable tagsview drag',
+    fourIsShareTagsView: 'Enable tagsview sharing',
+    fourIsFooter: 'Open footer',
+    fourIsGrayscale: 'Grey model',
+    fourIsInvert: 'Color weak mode',
+    fourIsDark: 'Dark Mode',
+    fourIsWartermark: 'Turn on watermark',
+    fourWartermarkText: 'Watermark copy',
+    fiveTitle: 'Other settings',
+    fiveTagsStyle: 'Tagsview style',
+    fiveAnimation: 'page animation',
+    fiveColumnsAsideStyle: 'Column style',
+    fiveColumnsAsideLayout: 'Column layout',
+    sixTitle: 'Layout switch',
+    sixDefaults: 'One',
+    sixClassic: 'Two',
+    sixTransverse: 'Three',
+    sixColumns: 'Four',
+    tipText: 'Click the button below to copy the layout configuration to `/src/stores/themeConfig.ts` It has been modified in.',
+    copyText: 'replication configuration',
+    resetText: 'restore default',
+    copyTextSuccess: 'Copy succeeded!',
+    copyTextError: 'Copy failed!',
+  },
+  upgrade: {
+    title: 'New version',
+    msg: 'The new version is available, please update it now! Dont worry, the update is fast!',
+    desc: 'Prompt: Update will restore the default configuration',
+    btnOne: 'Cruel refusal',
+    btnTwo: 'Update now',
+    btnTwoLoading: 'Updating',
+  },
+}

+ 192 - 0
src/i18n/lang/zh-cn.ts

@@ -0,0 +1,192 @@
+// 定义内容
+export default {
+  router: {
+    home: '首页',
+    system: '系统设置',
+    systemMenu: '菜单管理',
+    systemRole: '角色管理',
+    systemUser: '用户管理',
+    systemDept: '部门管理',
+    systemDic: '字典管理',
+    limits: '权限管理',
+    limitsFrontEnd: '前端控制',
+    limitsFrontEndPage: '页面权限',
+    limitsFrontEndBtn: '按钮权限',
+    limitsBackEnd: '后端控制',
+    limitsBackEndEndPage: '页面权限',
+    menu: '菜单嵌套',
+    menu1: '菜单1',
+    menu11: '菜单11',
+    menu12: '菜单12',
+    menu121: '菜单121',
+    menu122: '菜单122',
+    menu13: '菜单13',
+    menu2: '菜单2',
+    funIndex: '功能',
+    funTagsView: 'tagsView 操作',
+    funCountup: '数字滚动',
+    funWangEditor: 'Editor 编辑器',
+    funCropper: '图片裁剪',
+    funQrcode: '二维码生成',
+    funEchartsMap: '地理坐标/地图',
+    funPrintJs: '页面打印',
+    funClipboard: '复制剪切',
+    funGridLayout: '拖拽布局',
+    funSplitpanes: '窗格拆分器',
+    funDragVerify: '验证器',
+    pagesIndex: '页面',
+    pagesFiltering: '过滤筛选组件',
+    pagesFilteringDetails: '过滤筛选组件详情',
+    pagesFilteringDetails1: '过滤筛选组件详情111',
+    pagesIocnfont: 'ali 字体图标',
+    pagesElement: 'ele 字体图标',
+    pagesAwesome: 'awe 字体图标',
+    pagesFormAdapt: '表单自适应',
+    pagesTableRules: '表单表格验证',
+    pagesFormI18n: '表单国际化',
+    pagesFormRules: '多表单验证',
+    pagesDynamicForm: '动态复杂表单',
+    pagesWorkflow: '工作流',
+    pagesListAdapt: '列表自适应',
+    pagesWaterfall: '瀑布屏',
+    pagesSteps: '步骤条',
+    pagesPreview: '大图预览',
+    pagesWaves: '波浪效果',
+    pagesTree: '树形改表格',
+    pagesDrag: '拖动指令',
+    pagesLazyImg: '图片懒加载',
+    makeIndex: '组件封装',
+    makeSelector: '图标选择器',
+    makeNoticeBar: '滚动通知栏',
+    makeSvgDemo: 'svgIcon 演示',
+    makeTableDemo: '表格封装演示',
+    paramsIndex: '路由参数',
+    paramsCommon: '普通路由',
+    paramsDynamic: '动态路由',
+    paramsCommonDetails: '普通路由详情',
+    paramsDynamicDetails: '动态路由详情',
+    chartIndex: '大数据图表',
+    visualizingIndex: '数据可视化',
+    visualizingLinkDemo1: '数据可视化演示1',
+    visualizingLinkDemo2: '数据可视化演示2',
+    personal: '个人中心',
+    tools: '工具类集合',
+    layoutLinkView: '外链',
+    layoutIframeViewOne: '内嵌 iframe1',
+    layoutIframeViewTwo: '内嵌 iframe2',
+  },
+  staticRoutes: {
+    signIn: '登录',
+    notFound: '找不到此页面',
+    noPower: '没有权限',
+  },
+  user: {
+    title0: '组件大小',
+    title1: '语言切换',
+    title2: '菜单搜索',
+    title3: '布局配置',
+    title4: '消息',
+    title5: '开全屏',
+    title6: '关全屏',
+    dropdownLarge: '大型',
+    dropdownDefault: '默认',
+    dropdownSmall: '小型',
+    dropdown1: '首页',
+    dropdown2: '个人中心',
+    dropdown3: '404',
+    dropdown4: '401',
+    dropdown5: '退出登录',
+    dropdown6: '代码仓库',
+    searchPlaceholder: '菜单搜索:支持中文、路由路径',
+    newTitle: '通知',
+    newBtn: '全部已读',
+    newGo: '前往通知中心',
+    newDesc: '暂无通知',
+    logOutTitle: '提示',
+    logOutMessage: '此操作将退出登录, 是否继续?',
+    logOutConfirm: '确定',
+    logOutCancel: '取消',
+    logOutExit: '退出中',
+  },
+  tagsView: {
+    refresh: '刷新',
+    close: '关闭',
+    closeOther: '关闭其它',
+    closeAll: '全部关闭',
+    fullscreen: '当前页全屏',
+    closeFullscreen: '关闭全屏',
+  },
+  notFound: {
+    foundTitle: '地址输入错误,请重新输入地址~',
+    foundMsg: '您可以先检查网址,然后重新输入或给我们反馈问题。',
+    foundBtn: '返回首页',
+  },
+  noAccess: {
+    accessTitle: '您未被授权,没有操作权限~',
+    accessMsg: '联系方式:加QQ群探讨 665452019',
+    accessBtn: '重新授权',
+  },
+  layout: {
+    configTitle: '布局配置',
+    oneTitle: '全局主题',
+    twoTopTitle: '顶栏设置',
+    twoMenuTitle: '菜单设置',
+    twoColumnsTitle: '分栏设置',
+    twoTopBar: '顶栏背景',
+    twoTopBarColor: '顶栏默认字体颜色',
+    twoIsTopBarColorGradual: '顶栏背景渐变',
+    twoMenuBar: '菜单背景',
+    twoMenuBarColor: '菜单默认字体颜色',
+    twoMenuBarActiveColor: '菜单高亮背景色',
+    twoIsMenuBarColorGradual: '菜单背景渐变',
+    twoColumnsMenuBar: '分栏菜单背景',
+    twoColumnsMenuBarColor: '分栏菜单默认字体颜色',
+    twoIsColumnsMenuBarColorGradual: '分栏菜单背景渐变',
+    twoIsColumnsMenuHoverPreload: '分栏菜单鼠标悬停预加载',
+    threeTitle: '界面设置',
+    threeIsCollapse: '菜单水平折叠',
+    threeIsUniqueOpened: '菜单手风琴',
+    threeIsFixedHeader: '固定 Header',
+    threeIsClassicSplitMenu: '经典布局分割菜单',
+    threeIsLockScreen: '开启锁屏',
+    threeLockScreenTime: '自动锁屏(s/秒)',
+    fourTitle: '界面显示',
+    fourIsShowLogo: '侧边栏 Logo',
+    fourIsBreadcrumb: '开启 Breadcrumb',
+    fourIsBreadcrumbIcon: '开启 Breadcrumb 图标',
+    fourIsTagsview: '开启 Tagsview',
+    fourIsTagsviewIcon: '开启 Tagsview 图标',
+    fourIsCacheTagsView: '开启 TagsView 缓存',
+    fourIsSortableTagsView: '开启 TagsView 拖拽',
+    fourIsShareTagsView: '开启 TagsView 共用',
+    fourIsFooter: '开启 Footer',
+    fourIsGrayscale: '灰色模式',
+    fourIsInvert: '色弱模式',
+    fourIsDark: '深色模式',
+    fourIsWartermark: '开启水印',
+    fourWartermarkText: '水印文案',
+    fiveTitle: '其它设置',
+    fiveTagsStyle: 'Tagsview 风格',
+    fiveAnimation: '主页面切换动画',
+    fiveColumnsAsideStyle: '分栏高亮风格',
+    fiveColumnsAsideLayout: '分栏布局风格',
+    sixTitle: '布局切换',
+    sixDefaults: '默认',
+    sixClassic: '经典',
+    sixTransverse: '横向',
+    sixColumns: '分栏',
+    tipText: '点击下方按钮,复制布局配置去 `src/stores/themeConfig.ts` 中修改。',
+    copyText: '一键复制配置',
+    resetText: '一键恢复默认',
+    copyTextSuccess: '复制成功!',
+    copyTextError: '复制失败!',
+  },
+  upgrade: {
+    title: '新版本升级',
+    msg: '新版本来啦,马上更新尝鲜吧!不用担心,更新很快的哦!',
+    desc: '提示:更新会还原默认配置',
+    btnOne: '残忍拒绝',
+    btnTwo: '马上更新',
+    btnTwoLoading: '更新中',
+  },
+}

+ 192 - 0
src/i18n/lang/zh-tw.ts

@@ -0,0 +1,192 @@
+// 定义内容
+export default {
+  router: {
+    home: '首頁',
+    system: '系統設置',
+    systemMenu: '選單管理',
+    systemRole: '角色管理',
+    systemUser: '用戶管理',
+    systemDept: '部門管理',
+    systemDic: '字典管理',
+    limits: '許可權管理',
+    limitsFrontEnd: '前端控制',
+    limitsFrontEndPage: '頁面許可權',
+    limitsFrontEndBtn: '按鈕許可權',
+    limitsBackEnd: '後端控制',
+    limitsBackEndEndPage: '頁面許可權',
+    menu: '選單嵌套',
+    menu1: '選單1',
+    menu11: '選單11',
+    menu12: '選單12',
+    menu121: '選單121',
+    menu122: '選單122',
+    menu13: '選單13',
+    menu2: '選單2',
+    funIndex: '功能',
+    funTagsView: 'tagsView 操作',
+    funCountup: '數位滾動',
+    funWangEditor: 'Editor 編輯器',
+    funCropper: '圖片裁剪',
+    funQrcode: '二維碼生成',
+    funEchartsMap: '地理座標/地圖',
+    funPrintJs: '頁面列印',
+    funClipboard: '複製剪切',
+    funGridLayout: '拖拽佈局',
+    funSplitpanes: '窗格折開器',
+    funDragVerify: '驗證器',
+    pagesIndex: '頁面',
+    pagesFiltering: '過濾篩選組件',
+    pagesFilteringDetails: '過濾篩選組件詳情',
+    pagesFilteringDetails1: '過濾篩選組件詳情111',
+    pagesIocnfont: 'ali 字體圖標',
+    pagesElement: 'ele 字體圖標',
+    pagesAwesome: 'awe 字體圖標',
+    pagesFormAdapt: '表單自我調整',
+    pagesTableRules: '表單表格驗證',
+    pagesFormI18n: '表單國際化',
+    pagesFormRules: '多表單驗證',
+    pagesDynamicForm: '動態複雜表單',
+    pagesWorkflow: '工作流',
+    pagesListAdapt: '清單自我調整',
+    pagesWaterfall: '瀑布屏',
+    pagesSteps: '步驟條',
+    pagesPreview: '大圖預覽',
+    pagesWaves: '波浪效果',
+    pagesTree: '樹形改表格',
+    pagesDrag: '拖動指令',
+    pagesLazyImg: '圖片懶加載',
+    makeIndex: '組件封裝',
+    makeSelector: '圖標選擇器',
+    makeNoticeBar: '滾動通知欄',
+    makeSvgDemo: 'svgIcon 演示',
+    makeTableDemo: '表格封裝演示',
+    paramsIndex: '路由參數',
+    paramsCommon: '普通路由',
+    paramsDynamic: '動態路由',
+    paramsCommonDetails: '普通路由詳情',
+    paramsDynamicDetails: '動態路由詳情',
+    chartIndex: '大資料圖表',
+    visualizingIndex: '數據視覺化',
+    visualizingLinkDemo1: '數據視覺化演示1',
+    visualizingLinkDemo2: '數據視覺化演示2',
+    personal: '個人中心',
+    tools: '工具類集合',
+    layoutLinkView: '外鏈',
+    layoutIframeViewOne: '内嵌 iframe1',
+    layoutIframeViewTwo: '内嵌 iframe2',
+  },
+  staticRoutes: {
+    signIn: '登入',
+    notFound: '找不到此頁面',
+    noPower: '沒有許可權',
+  },
+  user: {
+    title0: '組件大小',
+    title1: '語言切換',
+    title2: '選單蒐索',
+    title3: '佈局配寘',
+    title4: '消息',
+    title5: '開全屏',
+    title6: '關全屏',
+    dropdownLarge: '大型',
+    dropdownDefault: '默認',
+    dropdownSmall: '小型',
+    dropdown1: '首頁',
+    dropdown2: '個人中心',
+    dropdown3: '404',
+    dropdown4: '401',
+    dropdown5: '登出',
+    dropdown6: '程式碼倉庫',
+    searchPlaceholder: '選單蒐索:支援中文、路由路徑',
+    newTitle: '通知',
+    newBtn: '全部已讀',
+    newGo: '前往通知中心',
+    newDesc: '暫無通知',
+    logOutTitle: '提示',
+    logOutMessage: '此操作將登出,是否繼續?',
+    logOutConfirm: '確定',
+    logOutCancel: '取消',
+    logOutExit: '退出中',
+  },
+  tagsView: {
+    refresh: '重繪',
+    close: '關閉',
+    closeOther: '關閉其它',
+    closeAll: '全部關閉',
+    fullscreen: '當前頁全屏',
+    closeFullscreen: '關閉全屏',
+  },
+  notFound: {
+    foundTitle: '地址輸入錯誤,請重新輸入地址~',
+    foundMsg: '您可以先檢查網址,然後重新輸入或給我們迴響問題。',
+    foundBtn: '返回首頁',
+  },
+  noAccess: {
+    accessTitle: '您未被授權,沒有操作許可權~',
+    accessMsg: '聯繫方式:加QQ群探討665452019',
+    accessBtn: '重新授權',
+  },
+  layout: {
+    configTitle: '佈局配寘',
+    oneTitle: '全域主題',
+    twoTopTitle: '頂欄設定',
+    twoMenuTitle: '選單設定',
+    twoColumnsTitle: '分欄設定',
+    twoTopBar: '頂欄背景',
+    twoTopBarColor: '頂欄默認字體顏色',
+    twoIsTopBarColorGradual: '頂欄背景漸變',
+    twoMenuBar: '選單背景',
+    twoMenuBarColor: '選單默認字體顏色',
+    twoMenuBarActiveColor: '選單高亮背景色',
+    twoIsMenuBarColorGradual: '選單背景漸變',
+    twoColumnsMenuBar: '分欄選單背景',
+    twoColumnsMenuBarColor: '分欄選單默認字體顏色',
+    twoIsColumnsMenuBarColorGradual: '分欄選單背景漸變',
+    twoIsColumnsMenuHoverPreload: '分欄選單滑鼠懸停預加載',
+    threeTitle: '介面設定',
+    threeIsCollapse: '選單水准折疊',
+    threeIsUniqueOpened: '選單手風琴',
+    threeIsFixedHeader: '固定 Header',
+    threeIsClassicSplitMenu: '經典佈局分割選單',
+    threeIsLockScreen: '開啟鎖屏',
+    threeLockScreenTime: '自動鎖屏(s/秒)',
+    fourTitle: '介面顯示',
+    fourIsShowLogo: '側邊欄 Logo',
+    fourIsBreadcrumb: '開啟 Breadcrumb',
+    fourIsBreadcrumbIcon: '開啟 Breadcrumb 圖標',
+    fourIsTagsview: '開啟 Tagsview',
+    fourIsTagsviewIcon: '開啟 Tagsview 圖標',
+    fourIsCacheTagsView: '開啟 TagsView 緩存',
+    fourIsSortableTagsView: '開啟 TagsView 拖拽',
+    fourIsShareTagsView: '開啟 TagsView 共用',
+    fourIsFooter: '開啟 Footer',
+    fourIsGrayscale: '灰色模式',
+    fourIsInvert: '色弱模式',
+    fourIsDark: '深色模式',
+    fourIsWartermark: '開啟浮水印',
+    fourWartermarkText: '浮水印文案',
+    fiveTitle: '其它設定',
+    fiveTagsStyle: 'Tagsview 風格',
+    fiveAnimation: '主頁面切換動畫',
+    fiveColumnsAsideStyle: '分欄高亮風格',
+    fiveColumnsAsideLayout: '分欄佈局風格',
+    sixTitle: '佈局切換',
+    sixDefaults: '默認',
+    sixClassic: '經典',
+    sixTransverse: '橫向',
+    sixColumns: '分欄',
+    tipText: '點擊下方按鈕,複製佈局配寘去`src/stores/themeConfig.ts`中修改。',
+    copyText: '一鍵複製配寘',
+    resetText: '一鍵恢復默認',
+    copyTextSuccess: '複製成功!',
+    copyTextError: '複製失敗!',
+  },
+  upgrade: {
+    title: '新版本陞級',
+    msg: '新版本來啦,馬上更新嘗鮮吧! 不用擔心,更新很快的哦!',
+    desc: '提示:更新會還原默認配寘',
+    btnOne: '殘忍拒絕',
+    btnTwo: '馬上更新',
+    btnTwoLoading: '更新中',
+  },
+}

+ 13 - 0
src/i18n/pages/formI18n/en.ts

@@ -0,0 +1,13 @@
+// 定义内容
+export default {
+  formI18nLabel: {
+    name: 'name',
+    email: 'email',
+    autograph: 'autograph',
+  },
+  formI18nPlaceholder: {
+    name: 'Please enter your name',
+    email: 'Please enter the users Department',
+    autograph: 'Please enter the login account name',
+  },
+}

+ 13 - 0
src/i18n/pages/formI18n/zh-cn.ts

@@ -0,0 +1,13 @@
+// 定义内容
+export default {
+  formI18nLabel: {
+    name: '姓名',
+    email: '用户归属部门',
+    autograph: '登陆账户名',
+  },
+  formI18nPlaceholder: {
+    name: '请输入姓名',
+    email: '请输入用户归属部门',
+    autograph: '请输入登陆账户名',
+  },
+}

+ 13 - 0
src/i18n/pages/formI18n/zh-tw.ts

@@ -0,0 +1,13 @@
+// 定义内容
+export default {
+  formI18nLabel: {
+    name: '姓名',
+    email: '用戶歸屬部門',
+    autograph: '登入帳戶名',
+  },
+  formI18nPlaceholder: {
+    name: '請輸入姓名',
+    email: '請輸入用戶歸屬部門',
+    autograph: '請輸入登入帳戶名',
+  },
+}

+ 29 - 0
src/i18n/pages/login/en.ts

@@ -0,0 +1,29 @@
+// 定义内容
+export default {
+  label: {
+    one1: 'User name login',
+    two2: 'Mobile number',
+  },
+  link: {
+    one3: 'Third party login',
+    two4: 'Links',
+  },
+  account: {
+    accountPlaceholder1: 'The user name admin or not is common',
+    accountPlaceholder2: 'Password: 123456',
+    accountPlaceholder3: 'Please enter the verification code',
+    accountBtnText: 'Sign in',
+  },
+  mobile: {
+    placeholder1: 'Please input mobile phone number',
+    placeholder2: 'Please enter the verification code',
+    codeText: 'Get code',
+    btnText: 'Sign in',
+    msgText:
+      'Warm tip: it is recommended to use Google, Microsoft edge, version 79.0.1072.62 and above browsers, and 360 browser, please use speed mode',
+  },
+  scan: {
+    text: 'Open the mobile phone to scan and quickly log in / register',
+  },
+  signInText: 'welcome back!',
+}

+ 28 - 0
src/i18n/pages/login/zh-cn.ts

@@ -0,0 +1,28 @@
+// 定义内容
+export default {
+  label: {
+    one1: '用户名登录',
+    two2: '手机号登录',
+  },
+  link: {
+    one3: '第三方登录',
+    two4: '友情链接',
+  },
+  account: {
+    accountPlaceholder1: '用户名 admin 或不输均为 common',
+    accountPlaceholder2: '密码:123456',
+    accountPlaceholder3: '请输入验证码',
+    accountBtnText: '登 录',
+  },
+  mobile: {
+    placeholder1: '请输入手机号',
+    placeholder2: '请输入验证码',
+    codeText: '获取验证码',
+    btnText: '登 录',
+    msgText: '* 温馨提示:建议使用谷歌、Microsoft Edge,版本 79.0.1072.62 及以上浏览器,360浏览器请使用极速模式',
+  },
+  scan: {
+    text: '打开手机扫一扫,快速登录/注册',
+  },
+  signInText: '欢迎回来!',
+}

+ 28 - 0
src/i18n/pages/login/zh-tw.ts

@@ -0,0 +1,28 @@
+// 定义内容
+export default {
+  label: {
+    one1: '用戶名登入',
+    two2: '手機號登入',
+  },
+  link: {
+    one3: '協力廠商登入',
+    two4: '友情連結',
+  },
+  account: {
+    accountPlaceholder1: '用戶名admin或不輸均為common',
+    accountPlaceholder2: '密碼:123456',
+    accountPlaceholder3: '請輸入驗證碼',
+    accountBtnText: '登入',
+  },
+  mobile: {
+    placeholder1: '請輸入手機號',
+    placeholder2: '請輸入驗證碼',
+    codeText: '獲取驗證碼',
+    btnText: '登入',
+    msgText: '* 溫馨提示:建議使用穀歌、Microsoft Edge,版本79.0.1072.62及以上瀏覽器,360瀏覽器請使用極速模式',
+  },
+  scan: {
+    text: '打開手機掃一掃,快速登錄/注册',
+  },
+  signInText: '歡迎回來!',
+}

+ 154 - 0
src/layout/component/aside.vue

@@ -0,0 +1,154 @@
+<template>
+  <div class="h100" v-show="!isTagsViewCurrenFull">
+    <el-aside class="layout-aside" :class="setCollapseStyle">
+      <Logo v-if="setShowLogo" />
+      <el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef" @mouseenter="onAsideEnterLeave(true)" @mouseleave="onAsideEnterLeave(false)">
+        <Vertical :menuList="state.menuList" />
+      </el-scrollbar>
+    </el-aside>
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutAside">
+import { defineAsyncComponent, reactive, computed, watch, onBeforeMount, ref } from 'vue'
+import { storeToRefs } from 'pinia'
+import pinia from '/@/stores/index'
+import { useRoutesList } from '/@/stores/routesList'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'
+import mittBus from '/@/utils/mitt'
+
+// 引入组件
+const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'))
+const Vertical = defineAsyncComponent(() => import('/@/layout/navMenu/vertical.vue'))
+
+// 定义变量内容
+const layoutAsideScrollbarRef = ref()
+const stores = useRoutesList()
+const storesThemeConfig = useThemeConfig()
+const storesTagsViewRoutes = useTagsViewRoutes()
+const { routesList } = storeToRefs(stores)
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes)
+const state = reactive<AsideState>({
+  menuList: [],
+  clientWidth: 0,
+})
+
+// 设置菜单展开/收起时的宽度
+const setCollapseStyle = computed(() => {
+  const { layout, isCollapse, menuBar } = themeConfig.value
+  const asideBrTheme = ['#FFFFFF', '#FFF', '#fff', '#ffffff']
+  const asideBrColor = asideBrTheme.includes(menuBar) ? 'layout-el-aside-br-color' : ''
+  // 判断是否是手机端
+  if (state.clientWidth <= 1000) {
+    if (isCollapse) {
+      document.body.setAttribute('class', 'el-popup-parent--hidden')
+      const asideEle = document.querySelector('.layout-container') as HTMLElement
+      const modeDivs = document.createElement('div')
+      modeDivs.setAttribute('class', 'layout-aside-mobile-mode')
+      asideEle.appendChild(modeDivs)
+      modeDivs.addEventListener('click', closeLayoutAsideMobileMode)
+      return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-open']
+    } else {
+      // 关闭弹窗
+      closeLayoutAsideMobileMode()
+      return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-close']
+    }
+  } else {
+    if (layout === 'columns') {
+      // 分栏布局,菜单收起时宽度给 1px
+      if (isCollapse) return [asideBrColor, 'layout-aside-pc-1']
+      else return [asideBrColor, 'layout-aside-pc-220']
+    } else {
+      // 其它布局给 64px
+      if (isCollapse) return [asideBrColor, 'layout-aside-pc-64']
+      else return [asideBrColor, 'layout-aside-pc-220']
+    }
+  }
+})
+// 设置显示/隐藏 logo
+const setShowLogo = computed(() => {
+  let { layout, isShowLogo } = themeConfig.value
+  return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns')
+})
+// 关闭移动端蒙版
+const closeLayoutAsideMobileMode = () => {
+  const el = document.querySelector('.layout-aside-mobile-mode')
+  el?.setAttribute('style', 'animation: error-img-two 0.3s')
+  setTimeout(() => {
+    el?.parentNode?.removeChild(el)
+  }, 300)
+  const clientWidth = document.body.clientWidth
+  if (clientWidth < 1000) themeConfig.value.isCollapse = false
+  document.body.setAttribute('class', '')
+}
+// 设置/过滤路由(非静态路由/是否显示在菜单中)
+const setFilterRoutes = () => {
+  if (themeConfig.value.layout === 'columns') return false
+  state.menuList = filterRoutesFun(routesList.value)
+}
+// 路由过滤递归函数
+const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
+  return arr
+    .filter((item: T) => !item.meta?.isHide)
+    .map((item: T) => {
+      item = Object.assign({}, item)
+      if (item.children) item.children = filterRoutesFun(item.children)
+      return item
+    })
+}
+// 设置菜单导航是否固定(移动端)
+const initMenuFixed = (clientWidth: number) => {
+  state.clientWidth = clientWidth
+}
+// 鼠标移入、移出
+const onAsideEnterLeave = (bool: Boolean) => {
+  let { layout } = themeConfig.value
+  if (layout !== 'columns') return false
+  if (!bool) mittBus.emit('restoreDefault')
+  stores.setColumnsMenuHover(bool)
+}
+// 页面加载前
+onBeforeMount(() => {
+  initMenuFixed(document.body.clientWidth)
+  setFilterRoutes()
+  // 此界面不需要取消监听(mittBus.off('setSendColumnsChildren))
+  // 因为切换布局时有的监听需要使用,取消了监听,某些操作将不生效
+  mittBus.on('setSendColumnsChildren', (res: MittMenu) => {
+    state.menuList = res.children
+  })
+  mittBus.on('setSendClassicChildren', (res: MittMenu) => {
+    let { layout, isClassicSplitMenu } = themeConfig.value
+    if (layout === 'classic' && isClassicSplitMenu) {
+      state.menuList = []
+      state.menuList = res.children
+    }
+  })
+  mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
+    setFilterRoutes()
+  })
+  mittBus.on('layoutMobileResize', (res: LayoutMobileResize) => {
+    initMenuFixed(res.clientWidth)
+    closeLayoutAsideMobileMode()
+  })
+})
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(themeConfig.value, (val) => {
+  if (val.isShowLogoChange !== val.isShowLogo) {
+    if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update()
+  }
+})
+// 监听 pinia 值的变化,动态赋值给菜单中
+watch(
+  pinia.state,
+  (val) => {
+    let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig
+    if (layout === 'classic' && isClassicSplitMenu) return false
+    setFilterRoutes()
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 272 - 0
src/layout/component/columnsAside.vue

@@ -0,0 +1,272 @@
+<template>
+  <div class="layout-columns-aside">
+    <el-scrollbar>
+      <ul @mouseleave="onColumnsAsideMenuMouseleave()">
+        <li
+          v-for="(v, k) in state.columnsAsideList"
+          :key="k"
+          @click="onColumnsAsideMenuClick(v, k)"
+          @mouseenter="onColumnsAsideMenuMouseenter(v, k)"
+          :ref="
+            (el) => {
+              if (el) columnsAsideOffsetTopRefs[k] = el
+            }
+          "
+          :class="{ 'layout-columns-active': state.liIndex === k, 'layout-columns-hover': state.liHoverIndex === k }"
+          :title="$t(v.meta.title)"
+        >
+          <div :class="themeConfig.columnsAsideLayout" v-if="!v.meta.isLink || (v.meta.isLink && v.meta.isIframe)">
+            <SvgIcon :name="v.meta.icon" />
+            <div class="columns-vertical-title font12">
+              {{
+                $t(v.meta.title) && $t(v.meta.title).length >= 4
+                  ? $t(v.meta.title).substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
+                  : $t(v.meta.title)
+              }}
+            </div>
+          </div>
+          <div :class="themeConfig.columnsAsideLayout" v-else>
+            <a :href="v.meta.isLink" target="_blank">
+              <SvgIcon :name="v.meta.icon" />
+              <div class="columns-vertical-title font12">
+                {{
+                  $t(v.meta.title) && $t(v.meta.title).length >= 4
+                    ? $t(v.meta.title).substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
+                    : $t(v.meta.title)
+                }}
+              </div>
+            </a>
+          </div>
+        </li>
+        <div ref="columnsAsideActiveRef" :class="themeConfig.columnsAsideStyle"></div>
+      </ul>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutColumnsAside">
+import { reactive, ref, onMounted, nextTick, watch, onUnmounted } from 'vue'
+import { useRoute, useRouter, onBeforeRouteUpdate, RouteRecordRaw } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import pinia from '/@/stores/index'
+import { useRoutesList } from '/@/stores/routesList'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import mittBus from '/@/utils/mitt'
+
+// 定义变量内容
+const columnsAsideOffsetTopRefs = ref<RefType>([])
+const columnsAsideActiveRef = ref()
+const stores = useRoutesList()
+const storesThemeConfig = useThemeConfig()
+const { routesList, isColumnsMenuHover, isColumnsNavHover } = storeToRefs(stores)
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const route = useRoute()
+const router = useRouter()
+const state = reactive<ColumnsAsideState>({
+  columnsAsideList: [],
+  liIndex: 0,
+  liOldIndex: null,
+  liHoverIndex: null,
+  liOldPath: null,
+  difference: 0,
+  routeSplit: [],
+})
+
+// 设置菜单高亮位置移动
+const setColumnsAsideMove = (k: number) => {
+  state.liIndex = k
+  columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`
+}
+// 菜单高亮点击事件
+const onColumnsAsideMenuClick = (v: RouteItem, k: number) => {
+  setColumnsAsideMove(k)
+  let { path, redirect } = v
+  if (redirect) router.push(redirect)
+  else router.push(path)
+}
+// 鼠标移入时,显示当前的子级菜单
+const onColumnsAsideMenuMouseenter = (v: RouteRecordRaw, k: number) => {
+  if (!themeConfig.value.isColumnsMenuHoverPreload) return false
+  let { path } = v
+  state.liOldPath = path
+  state.liOldIndex = k
+  state.liHoverIndex = k
+  mittBus.emit('setSendColumnsChildren', setSendChildren(path))
+  stores.setColumnsMenuHover(false)
+  stores.setColumnsNavHover(true)
+}
+// 鼠标移走时,显示原来的子级菜单
+const onColumnsAsideMenuMouseleave = async () => {
+  await stores.setColumnsNavHover(false)
+  // 添加延时器,防止拿到的 store.state.routesList 值不是最新的
+  setTimeout(() => {
+    if (!isColumnsMenuHover && !isColumnsNavHover) mittBus.emit('restoreDefault')
+  }, 100)
+}
+// 设置高亮动态位置
+const onColumnsAsideDown = (k: number) => {
+  nextTick(() => {
+    setColumnsAsideMove(k)
+  })
+}
+// 设置/过滤路由(非静态路由/是否显示在菜单中)
+const setFilterRoutes = () => {
+  state.columnsAsideList = filterRoutesFun(routesList.value)
+  const resData: MittMenu = setSendChildren(route.path)
+  if (Object.keys(resData).length <= 0) return false
+  onColumnsAsideDown(resData.item?.k)
+  mittBus.emit('setSendColumnsChildren', resData)
+}
+// 传送当前子级数据到菜单中
+const setSendChildren = (path: string) => {
+  const currentPathSplit = path.split('/')
+  let currentData: MittMenu = { children: [] }
+  state.columnsAsideList.map((v: RouteItem, k: number) => {
+    if (v.path === `/${currentPathSplit[1]}`) {
+      v['k'] = k
+      currentData['item'] = { ...v }
+      currentData['children'] = [{ ...v }]
+      if (v.children) currentData['children'] = v.children
+    }
+  })
+  return currentData
+}
+// 路由过滤递归函数
+const filterRoutesFun = <T extends RouteItem>(arr: T[]): T[] => {
+  return arr
+    .filter((item: T) => !item.meta?.isHide)
+    .map((item: T) => {
+      item = Object.assign({}, item)
+      if (item.children) item.children = filterRoutesFun(item.children)
+      return item
+    })
+}
+// tagsView 点击时,根据路由查找下标 columnsAsideList,实现左侧菜单高亮
+const setColumnsMenuHighlight = (path: string) => {
+  state.routeSplit = path.split('/')
+  state.routeSplit.shift()
+  const routeFirst = `/${state.routeSplit[0]}`
+  const currentSplitRoute = state.columnsAsideList.find((v: RouteItem) => v.path === routeFirst)
+  if (!currentSplitRoute) return false
+  // 延迟拿值,防止取不到
+  setTimeout(() => {
+    onColumnsAsideDown(currentSplitRoute.k)
+  }, 0)
+}
+// 页面加载时
+onMounted(() => {
+  setFilterRoutes()
+  // 销毁变量,防止鼠标再次移入时,保留了上次的记录
+  mittBus.on('restoreDefault', () => {
+    state.liOldIndex = null
+    state.liOldPath = null
+  })
+})
+// 页面卸载时
+onUnmounted(() => {
+  mittBus.off('restoreDefault', () => {})
+})
+// 路由更新时
+onBeforeRouteUpdate((to) => {
+  setColumnsMenuHighlight(to.path)
+  mittBus.emit('setSendColumnsChildren', setSendChildren(to.path))
+})
+// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
+watch(
+  pinia.state,
+  (val) => {
+    val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0)
+    if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
+      state.liHoverIndex = null
+      mittBus.emit('setSendColumnsChildren', setSendChildren(route.path))
+    } else {
+      state.liHoverIndex = state.liOldIndex
+      if (!state.liOldPath) return false
+      mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath))
+    }
+  },
+  {
+    deep: true,
+  }
+)
+</script>
+
+<style scoped lang="scss">
+.layout-columns-aside {
+  width: 70px;
+  height: 100%;
+  background: var(--next-bg-columnsMenuBar);
+  ul {
+    position: relative;
+    .layout-columns-active {
+      color: var(--next-bg-columnsMenuBarColor) !important;
+      transition: 0.3s ease-in-out;
+    }
+    .layout-columns-hover {
+      color: var(--el-color-primary);
+      a {
+        color: var(--el-color-primary);
+      }
+    }
+    li {
+      color: var(--next-bg-columnsMenuBarColor);
+      width: 100%;
+      height: 50px;
+      text-align: center;
+      display: flex;
+      cursor: pointer;
+      position: relative;
+      z-index: 1;
+      &:hover {
+        @extend .layout-columns-hover;
+      }
+      .columns-vertical {
+        margin: auto;
+        .columns-vertical-title {
+          padding-top: 1px;
+        }
+      }
+      .columns-horizontal {
+        display: flex;
+        height: 50px;
+        width: 100%;
+        align-items: center;
+        padding: 0 5px;
+        i {
+          margin-right: 3px;
+        }
+        a {
+          display: flex;
+          .columns-horizontal-title {
+            padding-top: 1px;
+          }
+        }
+      }
+      a {
+        text-decoration: none;
+        color: var(--next-bg-columnsMenuBarColor);
+      }
+    }
+    .columns-round {
+      background: var(--el-color-primary);
+      color: var(--el-color-white);
+      position: absolute;
+      left: 50%;
+      top: 2px;
+      height: 44px;
+      width: 65px;
+      transform: translateX(-50%);
+      z-index: 0;
+      transition: 0.3s ease-in-out;
+      border-radius: 5px;
+    }
+    .columns-card {
+      @extend .columns-round;
+      top: 0;
+      height: 50px;
+      width: 100%;
+      border-radius: 0;
+    }
+  }
+}
+</style>

+ 18 - 0
src/layout/component/header.vue

@@ -0,0 +1,18 @@
+<template>
+  <el-header class="layout-header" v-show="!isTagsViewCurrenFull">
+    <NavBarsIndex />
+  </el-header>
+</template>
+
+<script setup lang="ts" name="layoutHeader">
+import { defineAsyncComponent } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'
+
+// 引入组件
+const NavBarsIndex = defineAsyncComponent(() => import('/@/layout/navBars/index.vue'))
+
+// 定义变量内容
+const storesTagsViewRoutes = useTagsViewRoutes()
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes)
+</script>

+ 65 - 0
src/layout/component/main.vue

@@ -0,0 +1,65 @@
+<template>
+  <el-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
+    <el-scrollbar
+      ref="layoutMainScrollbarRef"
+      class="layout-main-scroll layout-backtop-header-fixed"
+      wrap-class="layout-main-scroll"
+      view-class="layout-main-scroll"
+    >
+      <LayoutParentView />
+      <LayoutFooter v-if="isFooter" />
+    </el-scrollbar>
+    <el-backtop :target="setBacktopClass" />
+  </el-main>
+</template>
+
+<script setup lang="ts" name="layoutMain">
+import { defineAsyncComponent, onMounted, computed, ref } from 'vue'
+import { useRoute } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import { NextLoading } from '/@/utils/loading'
+
+// 引入组件
+const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue'))
+const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue'))
+
+// 定义变量内容
+const layoutMainScrollbarRef = ref()
+const route = useRoute()
+const storesTagsViewRoutes = useTagsViewRoutes()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes)
+
+// 设置 footer 显示/隐藏
+const isFooter = computed(() => {
+  return themeConfig.value.isFooter && !route.meta.isIframe
+})
+// 设置 header 固定
+const isFixedHeader = computed(() => {
+  return themeConfig.value.isFixedHeader
+})
+// 设置 Backtop 回到顶部
+const setBacktopClass = computed(() => {
+  if (themeConfig.value.isFixedHeader) return `.layout-backtop-header-fixed .el-scrollbar__wrap`
+  else return `.layout-backtop .el-scrollbar__wrap`
+})
+// 设置主内容区的高度
+const setMainHeight = computed(() => {
+  if (isTagsViewCurrenFull.value) return '0px'
+  const { isTagsview, layout } = themeConfig.value
+  if (isTagsview && layout !== 'classic') return '85px'
+  else return '51px'
+})
+// 页面加载前
+onMounted(() => {
+  NextLoading.done(600)
+})
+
+// 暴露变量
+defineExpose({
+  layoutMainScrollbarRef,
+})
+</script>

+ 25 - 0
src/layout/footer/index.vue

@@ -0,0 +1,25 @@
+<template>
+  <div class="layout-footer pb15">
+    <div class="layout-footer-warp">
+      <div>中台Admin</div>
+      <div class="mt5">Copyright © 2022 中台Admin All rights reserved.</div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutFooter">
+// 此处需有内容(注释也得),否则缓存将失败
+</script>
+
+<style scoped lang="scss">
+.layout-footer {
+  width: 100%;
+  display: flex;
+  &-warp {
+    margin: auto;
+    color: var(--el-text-color-secondary);
+    text-align: center;
+    animation: error-num 0.3s ease;
+  }
+}
+</style>

+ 50 - 0
src/layout/index.vue

@@ -0,0 +1,50 @@
+<template>
+  <component :is="layouts[themeConfig.layout]" />
+</template>
+
+<script setup lang="ts" name="layout">
+import { onBeforeMount, onUnmounted, defineAsyncComponent } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import { Local } from '/@/utils/storage'
+import mittBus from '/@/utils/mitt'
+
+// 引入组件
+const layouts: any = {
+  defaults: defineAsyncComponent(() => import('/@/layout/main/defaults.vue')),
+  classic: defineAsyncComponent(() => import('/@/layout/main/classic.vue')),
+  transverse: defineAsyncComponent(() => import('/@/layout/main/transverse.vue')),
+  columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
+}
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+
+// 窗口大小改变时(适配移动端)
+const onLayoutResize = () => {
+  if (!Local.get('oldLayout')) Local.set('oldLayout', themeConfig.value.layout)
+  const clientWidth = document.body.clientWidth
+  if (clientWidth < 1000) {
+    themeConfig.value.isCollapse = false
+    mittBus.emit('layoutMobileResize', {
+      layout: 'defaults',
+      clientWidth,
+    })
+  } else {
+    mittBus.emit('layoutMobileResize', {
+      layout: Local.get('oldLayout') ? Local.get('oldLayout') : themeConfig.value.layout,
+      clientWidth,
+    })
+  }
+}
+// 页面加载前
+onBeforeMount(() => {
+  onLayoutResize()
+  window.addEventListener('resize', onLayoutResize)
+})
+// 页面卸载时
+onUnmounted(() => {
+  window.removeEventListener('resize', onLayoutResize)
+})
+</script>

+ 352 - 0
src/layout/lockScreen/index.vue

@@ -0,0 +1,352 @@
+<template>
+  <div v-show="state.isShowLockScreen">
+    <div class="layout-lock-screen-mask"></div>
+    <div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': state.isShowLoockLogin }"></div>
+    <div class="layout-lock-screen">
+      <div
+        class="layout-lock-screen-date"
+        ref="layoutLockScreenDateRef"
+        @mousedown="onDownPc"
+        @mousemove="onMovePc"
+        @mouseup="onEnd"
+        @touchstart.stop="onDownApp"
+        @touchmove.stop="onMoveApp"
+        @touchend.stop="onEnd"
+      >
+        <div class="layout-lock-screen-date-box">
+          <div class="layout-lock-screen-date-box-time">
+            {{ state.time.hm }}<span class="layout-lock-screen-date-box-minutes">{{ state.time.s }}</span>
+          </div>
+          <div class="layout-lock-screen-date-box-info">{{ state.time.mdq }}</div>
+        </div>
+        <div class="layout-lock-screen-date-top">
+          <SvgIcon name="ele-Top" />
+          <div class="layout-lock-screen-date-top-text">上滑解锁</div>
+        </div>
+      </div>
+      <transition name="el-zoom-in-center">
+        <div v-show="state.isShowLoockLogin" class="layout-lock-screen-login">
+          <div class="layout-lock-screen-login-box">
+            <div class="layout-lock-screen-login-box-img">
+              <img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
+            </div>
+            <div class="layout-lock-screen-login-box-name">Administrator</div>
+            <div class="layout-lock-screen-login-box-value">
+              <el-input
+                placeholder="请输入密码"
+                ref="layoutLockScreenInputRef"
+                v-model="state.lockScreenPassword"
+                @keyup.enter.native.stop="onLockScreenSubmit()"
+              >
+                <template #append>
+                  <el-button @click="onLockScreenSubmit">
+                    <el-icon class="el-input__icon">
+                      <ele-Right />
+                    </el-icon>
+                  </el-button>
+                </template>
+              </el-input>
+            </div>
+          </div>
+          <div class="layout-lock-screen-login-icon">
+            <SvgIcon name="ele-Microphone" :size="20" />
+            <SvgIcon name="ele-AlarmClock" :size="20" />
+            <SvgIcon name="ele-SwitchButton" :size="20" />
+          </div>
+        </div>
+      </transition>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutLockScreen">
+import { nextTick, onMounted, reactive, ref, onUnmounted } from 'vue'
+import { formatDate } from '/@/utils/formatTime'
+import { Local } from '/@/utils/storage'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+
+// 定义变量内容
+const layoutLockScreenDateRef = ref<HtmlType>()
+const layoutLockScreenInputRef = ref()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const state = reactive({
+  transparency: 1,
+  downClientY: 0,
+  moveDifference: 0,
+  isShowLoockLogin: false,
+  isFlags: false,
+  querySelectorEl: '' as HtmlType,
+  time: {
+    hm: '',
+    s: '',
+    mdq: '',
+  },
+  setIntervalTime: 0,
+  isShowLockScreen: false,
+  isShowLockScreenIntervalTime: 0,
+  lockScreenPassword: '',
+})
+
+// 鼠标按下 pc
+const onDownPc = (down: MouseEvent) => {
+  state.isFlags = true
+  state.downClientY = down.clientY
+}
+// 鼠标按下 app
+const onDownApp = (down: TouchEvent) => {
+  state.isFlags = true
+  state.downClientY = down.touches[0].clientY
+}
+// 鼠标移动 pc
+const onMovePc = (move: MouseEvent) => {
+  state.moveDifference = move.clientY - state.downClientY
+  onMove()
+}
+// 鼠标移动 app
+const onMoveApp = (move: TouchEvent) => {
+  state.moveDifference = move.touches[0].clientY - state.downClientY
+  onMove()
+}
+// 鼠标移动事件
+const onMove = () => {
+  if (state.isFlags) {
+    const el = <HTMLElement>state.querySelectorEl
+    const opacitys = (state.transparency -= 1 / 200)
+    if (state.moveDifference >= 0) return false
+    el.setAttribute('style', `top:${state.moveDifference}px;cursor:pointer;opacity:${opacitys};`)
+    if (state.moveDifference < -400) {
+      el.setAttribute('style', `top:${-el.clientHeight}px;cursor:pointer;transition:all 0.3s ease;`)
+      state.moveDifference = -el.clientHeight
+      setTimeout(() => {
+        el && el.parentNode?.removeChild(el)
+      }, 300)
+    }
+    if (state.moveDifference === -el.clientHeight) {
+      state.isShowLoockLogin = true
+      layoutLockScreenInputRef.value.focus()
+    }
+  }
+}
+// 鼠标松开
+const onEnd = () => {
+  state.isFlags = false
+  state.transparency = 1
+  if (state.moveDifference >= -400) {
+    ;(<HTMLElement>state.querySelectorEl).setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`)
+  }
+}
+// 获取要拖拽的初始元素
+const initGetElement = () => {
+  nextTick(() => {
+    state.querySelectorEl = layoutLockScreenDateRef.value
+  })
+}
+// 时间初始化
+const initTime = () => {
+  state.time.hm = formatDate(new Date(), 'HH:MM')
+  state.time.s = formatDate(new Date(), 'SS')
+  state.time.mdq = formatDate(new Date(), 'mm月dd日,WWW')
+}
+// 时间初始化定时器
+const initSetTime = () => {
+  initTime()
+  state.setIntervalTime = window.setInterval(() => {
+    initTime()
+  }, 1000)
+}
+// 锁屏时间定时器
+const initLockScreen = () => {
+  if (themeConfig.value.isLockScreen) {
+    state.isShowLockScreenIntervalTime = window.setInterval(() => {
+      if (themeConfig.value.lockScreenTime <= 1) {
+        state.isShowLockScreen = true
+        setLocalThemeConfig()
+        return false
+      }
+      themeConfig.value.lockScreenTime--
+    }, 1000)
+  } else {
+    clearInterval(state.isShowLockScreenIntervalTime)
+  }
+}
+// 存储布局配置
+const setLocalThemeConfig = () => {
+  themeConfig.value.isDrawer = false
+  Local.set('themeConfig', themeConfig.value)
+}
+// 密码输入点击事件
+const onLockScreenSubmit = () => {
+  themeConfig.value.isLockScreen = false
+  themeConfig.value.lockScreenTime = 30
+  setLocalThemeConfig()
+}
+// 页面加载时
+onMounted(() => {
+  initGetElement()
+  initSetTime()
+  initLockScreen()
+})
+// 页面卸载时
+onUnmounted(() => {
+  window.clearInterval(state.setIntervalTime)
+  window.clearInterval(state.isShowLockScreenIntervalTime)
+})
+</script>
+
+<style scoped lang="scss">
+.layout-lock-screen-fixed {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+}
+.layout-lock-screen-filter {
+  filter: blur(1px);
+}
+.layout-lock-screen-mask {
+  background: var(--el-color-white);
+  @extend .layout-lock-screen-fixed;
+  z-index: 9999990;
+}
+.layout-lock-screen-img {
+  @extend .layout-lock-screen-fixed;
+  background-image: url('https://img-blog.csdnimg.cn/afa9c317667f47d5bea34b85af45979e.png#pic_center');
+  background-size: 100% 100%;
+  z-index: 9999991;
+}
+.layout-lock-screen {
+  @extend .layout-lock-screen-fixed;
+  z-index: 9999992;
+  &-date {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    color: var(--el-color-white);
+    z-index: 9999993;
+    user-select: none;
+    &-box {
+      position: absolute;
+      left: 30px;
+      bottom: 50px;
+      &-time {
+        font-size: 100px;
+        color: var(--el-color-white);
+      }
+      &-info {
+        font-size: 40px;
+        color: var(--el-color-white);
+      }
+      &-minutes {
+        font-size: 16px;
+      }
+    }
+    &-top {
+      width: 40px;
+      height: 40px;
+      line-height: 40px;
+      border-radius: 100%;
+      border: 1px solid var(--el-border-color-light, #ebeef5);
+      background: rgba(255, 255, 255, 0.1);
+      color: var(--el-color-white);
+      opacity: 0.8;
+      position: absolute;
+      right: 30px;
+      bottom: 50px;
+      text-align: center;
+      overflow: hidden;
+      transition: all 0.3s ease;
+      i {
+        transition: all 0.3s ease;
+      }
+      &-text {
+        opacity: 0;
+        position: absolute;
+        top: 150%;
+        font-size: 12px;
+        color: var(--el-color-white);
+        left: 50%;
+        line-height: 1.2;
+        transform: translate(-50%, -50%);
+        transition: all 0.3s ease;
+        width: 35px;
+      }
+      &:hover {
+        border: 1px solid rgba(255, 255, 255, 0.5);
+        background: rgba(255, 255, 255, 0.2);
+        box-shadow: 0 0 12px 0 rgba(255, 255, 255, 0.5);
+        color: var(--el-color-white);
+        opacity: 1;
+        transition: all 0.3s ease;
+        i {
+          transform: translateY(-40px);
+          transition: all 0.3s ease;
+        }
+        .layout-lock-screen-date-top-text {
+          opacity: 1;
+          top: 50%;
+          transition: all 0.3s ease;
+        }
+      }
+    }
+  }
+  &-login {
+    position: relative;
+    z-index: 9999994;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    top: 0;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    color: var(--el-color-white);
+    &-box {
+      text-align: center;
+      margin: auto;
+      &-img {
+        width: 180px;
+        height: 180px;
+        margin: auto;
+        img {
+          width: 100%;
+          height: 100%;
+          border-radius: 100%;
+        }
+      }
+      &-name {
+        font-size: 26px;
+        margin: 15px 0 30px;
+      }
+    }
+    &-icon {
+      position: absolute;
+      right: 30px;
+      bottom: 30px;
+      i {
+        font-size: 20px;
+        margin-left: 15px;
+        cursor: pointer;
+        opacity: 0.8;
+        &:hover {
+          opacity: 1;
+        }
+      }
+    }
+  }
+}
+:deep(.el-input-group__append) {
+  background: var(--el-color-white);
+  padding: 0px 15px;
+}
+:deep(.el-input__inner) {
+  border-right-color: var(--el-border-color-extra-light);
+  &:hover {
+    border-color: var(--el-border-color-extra-light);
+  }
+}
+</style>

+ 75 - 0
src/layout/logo/index.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
+    <img :src="logoMini" class="layout-logo-medium-img" />
+    <span>{{ themeConfig.globalTitle }}</span>
+  </div>
+  <div class="layout-logo-size" v-else @click="onThemeConfigChange">
+    <img :src="logoMini" class="layout-logo-size-img" />
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutLogo">
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import logoMini from '/@/assets/logo-mini.svg'
+
+// 定义变量内容
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+
+// 设置 logo 的显示。classic 经典布局默认显示 logo
+const setShowLogo = computed(() => {
+  let { isCollapse, layout } = themeConfig.value
+  return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000
+})
+// logo 点击实现菜单展开/收起
+const onThemeConfigChange = () => {
+  if (themeConfig.value.layout === 'transverse') return false
+  themeConfig.value.isCollapse = !themeConfig.value.isCollapse
+}
+</script>
+
+<style scoped lang="scss">
+.layout-logo {
+  width: 220px;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  box-shadow: rgb(0 21 41 / 2%) 0px 1px 4px;
+  color: var(--el-color-primary);
+  font-size: 16px;
+  cursor: pointer;
+  animation: logoAnimation 0.3s ease-in-out;
+  span {
+    white-space: nowrap;
+    display: inline-block;
+  }
+  &:hover {
+    span {
+      color: var(--color-primary-light-2);
+    }
+  }
+  &-medium-img {
+    width: 20px;
+    margin-right: 5px;
+  }
+}
+.layout-logo-size {
+  width: 100%;
+  height: 50px;
+  display: flex;
+  cursor: pointer;
+  animation: logoAnimation 0.3s ease-in-out;
+  &-img {
+    width: 20px;
+    margin: auto;
+  }
+  &:hover {
+    img {
+      animation: logoAnimation 0.3s ease-in-out;
+    }
+  }
+}
+</style>

+ 71 - 0
src/layout/main/classic.vue

@@ -0,0 +1,71 @@
+<template>
+  <el-container class="layout-container flex-center">
+    <LayoutHeader />
+    <el-container class="layout-mian-height-50">
+      <LayoutAside />
+      <div class="flex-center layout-backtop">
+        <LayoutTagsView v-if="isTagsview" />
+        <LayoutMain ref="layoutMainRef" />
+      </div>
+    </el-container>
+  </el-container>
+</template>
+
+<script setup lang="ts" name="layoutClassic">
+import { defineAsyncComponent, computed, ref, watch, nextTick, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+
+// 引入组件
+const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'))
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'))
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'))
+const LayoutTagsView = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue'))
+
+// 定义变量内容
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>()
+const route = useRoute()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+
+// 判断是否显示 tasgview
+const isTagsview = computed(() => {
+  return themeConfig.value.isTagsview
+})
+// 重置滚动条高度,更新子级 scrollbar
+const updateScrollbar = () => {
+  layoutMainRef.value?.layoutMainScrollbarRef.update()
+}
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+  nextTick(() => {
+    setTimeout(() => {
+      updateScrollbar()
+      // '!' not null 断言操作符,不执行运行时检查
+      layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0
+    }, 500)
+  })
+}
+// 页面加载时
+onMounted(() => {
+  initScrollBarHeight()
+})
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+  () => route.path,
+  () => {
+    initScrollBarHeight()
+  }
+)
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+  themeConfig,
+  () => {
+    updateScrollbar()
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 71 - 0
src/layout/main/columns.vue

@@ -0,0 +1,71 @@
+<template>
+  <el-container class="layout-container">
+    <ColumnsAside />
+    <el-container class="layout-columns-warp layout-container-view h100">
+      <LayoutAside />
+      <el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
+        <LayoutHeader />
+        <LayoutMain ref="layoutMainRef" />
+      </el-scrollbar>
+    </el-container>
+  </el-container>
+</template>
+
+<script setup lang="ts" name="layoutColumns">
+import { defineAsyncComponent, watch, onMounted, nextTick, ref } from 'vue'
+import { useRoute } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+
+// 引入组件
+const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'))
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'))
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'))
+const ColumnsAside = defineAsyncComponent(() => import('/@/layout/component/columnsAside.vue'))
+
+// 定义变量内容
+const layoutScrollbarRef = ref<RefType>('')
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>()
+const route = useRoute()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+
+// 重置滚动条高度
+const updateScrollbar = () => {
+  // 更新父级 scrollbar
+  layoutScrollbarRef.value.update()
+  // 更新子级 scrollbar
+  layoutMainRef.value!.layoutMainScrollbarRef.update()
+}
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+  nextTick(() => {
+    setTimeout(() => {
+      updateScrollbar()
+      layoutScrollbarRef.value.wrapRef.scrollTop = 0
+      layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0
+    }, 500)
+  })
+}
+// 页面加载时
+onMounted(() => {
+  initScrollBarHeight()
+})
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+  () => route.path,
+  () => {
+    initScrollBarHeight()
+  }
+)
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+  themeConfig,
+  () => {
+    updateScrollbar()
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 71 - 0
src/layout/main/defaults.vue

@@ -0,0 +1,71 @@
+<template>
+  <el-container class="layout-container">
+    <LayoutAside />
+    <el-container class="layout-container-view h100">
+      <el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
+        <LayoutHeader />
+        <LayoutMain ref="layoutMainRef" />
+      </el-scrollbar>
+    </el-container>
+  </el-container>
+</template>
+
+<script setup lang="ts" name="layoutDefaults">
+import { defineAsyncComponent, watch, onMounted, nextTick, ref } from 'vue'
+import { useRoute } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import { NextLoading } from '/@/utils/loading'
+
+// 引入组件
+const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'))
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'))
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'))
+
+// 定义变量内容
+const layoutScrollbarRef = ref<RefType>('')
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>()
+const route = useRoute()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+
+// 重置滚动条高度
+const updateScrollbar = () => {
+  // 更新父级 scrollbar
+  layoutScrollbarRef.value.update()
+  // 更新子级 scrollbar
+  layoutMainRef.value!.layoutMainScrollbarRef.update()
+}
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+  nextTick(() => {
+    setTimeout(() => {
+      updateScrollbar()
+      layoutScrollbarRef.value.wrapRef.scrollTop = 0
+      layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0
+    }, 500)
+  })
+}
+// 页面加载时
+onMounted(() => {
+  initScrollBarHeight()
+  NextLoading.done(600)
+})
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+  () => route.path,
+  () => {
+    initScrollBarHeight()
+  }
+)
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+  themeConfig,
+  () => {
+    updateScrollbar()
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 58 - 0
src/layout/main/transverse.vue

@@ -0,0 +1,58 @@
+<template>
+  <el-container class="layout-container flex-center layout-backtop">
+    <LayoutHeader />
+    <LayoutMain ref="layoutMainRef" />
+  </el-container>
+</template>
+
+<script setup lang="ts" name="layoutTransverse">
+import { defineAsyncComponent, ref, watch, nextTick, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+
+// 引入组件
+const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'))
+const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'))
+
+// 定义变量内容
+const layoutMainRef = ref<InstanceType<typeof LayoutMain>>()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const route = useRoute()
+
+// 重置滚动条高度,更新子级 scrollbar
+const updateScrollbar = () => {
+  layoutMainRef.value!.layoutMainScrollbarRef.update()
+}
+// 重置滚动条高度,由于组件是异步引入的
+const initScrollBarHeight = () => {
+  nextTick(() => {
+    setTimeout(() => {
+      updateScrollbar()
+      layoutMainRef.value!.layoutMainScrollbarRef.wrapRef.scrollTop = 0
+    }, 500)
+  })
+}
+// 页面加载时
+onMounted(() => {
+  initScrollBarHeight()
+})
+// 监听路由的变化,切换界面时,滚动条置顶
+watch(
+  () => route.path,
+  () => {
+    initScrollBarHeight()
+  }
+)
+// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
+watch(
+  themeConfig,
+  () => {
+    updateScrollbar()
+  },
+  {
+    deep: true,
+  }
+)
+</script>

+ 146 - 0
src/layout/navBars/breadcrumb/breadcrumb.vue

@@ -0,0 +1,146 @@
+<template>
+  <div v-if="isShowBreadcrumb" class="layout-navbars-breadcrumb">
+    <SvgIcon
+      class="layout-navbars-breadcrumb-icon"
+      :name="themeConfig.isCollapse ? 'ele-Expand' : 'ele-Fold'"
+      :size="16"
+      @click="onThemeConfigChange"
+    />
+    <el-breadcrumb class="layout-navbars-breadcrumb-hide">
+      <transition-group name="breadcrumb">
+        <el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
+          <span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
+            <SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />
+            <div v-if="!v.meta.tagsViewName">{{ $t(v.meta.title) }}</div>
+            <div v-else>{{ v.meta.tagsViewName }}</div>
+          </span>
+          <a v-else @click.prevent="onBreadcrumbClick(v)">
+            <SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />{{ $t(v.meta.title) }}
+          </a>
+        </el-breadcrumb-item>
+      </transition-group>
+    </el-breadcrumb>
+  </div>
+</template>
+
+<script setup lang="ts" name="layoutBreadcrumb">
+import { reactive, computed, onMounted } from 'vue'
+import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
+import { Local } from '/@/utils/storage'
+import other from '/@/utils/other'
+import { storeToRefs } from 'pinia'
+import { useThemeConfig } from '/@/stores/themeConfig'
+import { useRoutesList } from '/@/stores/routesList'
+
+// 定义变量内容
+const stores = useRoutesList()
+const storesThemeConfig = useThemeConfig()
+const { themeConfig } = storeToRefs(storesThemeConfig)
+const { routesList } = storeToRefs(stores)
+const route = useRoute()
+const router = useRouter()
+const state = reactive<BreadcrumbState>({
+  breadcrumbList: [],
+  routeSplit: [],
+  routeSplitFirst: '',
+  routeSplitIndex: 1,
+})
+
+// 动态设置经典、横向布局不显示
+const isShowBreadcrumb = computed(() => {
+  initRouteSplit(route.path)
+  const { layout, isBreadcrumb } = themeConfig.value
+  if (layout === 'classic' || layout === 'transverse') return false
+  else return isBreadcrumb ? true : false
+})
+// 面包屑点击时
+const onBreadcrumbClick = (v: RouteItem) => {
+  const { redirect, path } = v
+  if (redirect) router.push(redirect)
+  else router.push(path)
+}
+// 展开/收起左侧菜单点击
+const onThemeConfigChange = () => {
+  themeConfig.value.isCollapse = !themeConfig.value.isCollapse
+  setLocalThemeConfig()
+}
+// 存储布局配置
+const setLocalThemeConfig = () => {
+  Local.remove('themeConfig')
+  Local.set('themeConfig', themeConfig.value)
+}
+// 处理面包屑数据
+const getBreadcrumbList = (arr: RouteItems) => {
+  arr.forEach((item: RouteItem) => {
+    state.routeSplit.forEach((v: string, k: number, arrs: string[]) => {
+      if (state.routeSplitFirst === item.path) {
+        state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`
+        state.breadcrumbList.push(item)
+        state.routeSplitIndex++
+        if (item.children) getBreadcrumbList(item.children)
+      }
+    })
+  })
+}
+// 当前路由字符串切割成数组,并删除第一项空内容
+const initRouteSplit = (path: string) => {
+  if (!themeConfig.value.isBreadcrumb) return false
+  state.breadcrumbList = [] //[routesList.value[0]]
+  state.routeSplit = path.split('/')
+  state.routeSplit.shift()
+  state.routeSplitFirst = `/${state.routeSplit[0]}`
+  state.routeSplitIndex = 1
+  getBreadcrumbList(routesList.value)
+  if (route.name === 'home' || (route.name === 'notFound' && state.breadcrumbList.length > 1)) state.breadcrumbList.shift()
+  if (state.breadcrumbList.length > 0)
+    state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(<RouteToFrom>route)
+}
+// 页面加载时
+onMounted(() => {
+  initRouteSplit(route.path)
+})
+// 路由更新时
+onBeforeRouteUpdate((to) => {
+  initRouteSplit(to.path)
+})
+</script>
+
+<style scoped lang="scss">
+.layout-navbars-breadcrumb {
+  flex: 1;
+  height: inherit;
+  display: flex;
+  align-items: center;
+  .layout-navbars-breadcrumb-icon {
+    cursor: pointer;
+    font-size: 18px;
+    color: var(--next-bg-topBarColor);
+    height: 100%;
+    width: 40px;
+    opacity: 0.8;
+    &:hover {
+      opacity: 1;
+    }
+  }
+  .layout-navbars-breadcrumb-span {
+    display: flex;
+    opacity: 0.7;
+    color: var(--next-bg-topBarColor);
+  }
+  .layout-navbars-breadcrumb-iconfont {
+    font-size: 14px;
+    margin-right: 5px;
+  }
+  :deep(.el-breadcrumb__separator) {
+    opacity: 0.7;
+    color: var(--next-bg-topBarColor);
+  }
+  :deep(.el-breadcrumb__inner a, .el-breadcrumb__inner.is-link) {
+    font-weight: unset !important;
+    color: var(--next-bg-topBarColor);
+    &:hover {
+      color: var(--el-color-primary) !important;
+    }
+  }
+}
+</style>

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