Parcourir la source

!288 修复vue3 部分bug 重构axios 完善vxe demo
Merge pull request !288 from xingyu/dev

芋道源码 il y a 2 ans
Parent
commit
cb6a9a1282
68 fichiers modifiés avec 1352 ajouts et 1335 suppressions
  1. 2 2
      yudao-dependencies/pom.xml
  2. 1 3
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm
  3. 3 3
      yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm
  4. 1 1
      yudao-ui-admin-vue3/.env
  5. 7 7
      yudao-ui-admin-vue3/package.json
  6. 162 134
      yudao-ui-admin-vue3/pnpm-lock.yaml
  7. 1 2
      yudao-ui-admin-vue3/src/api/bpm/form/index.ts
  8. 1 2
      yudao-ui-admin-vue3/src/api/bpm/leave/index.ts
  9. 1 2
      yudao-ui-admin-vue3/src/api/bpm/model/index.ts
  10. 1 2
      yudao-ui-admin-vue3/src/api/bpm/processInstance/index.ts
  11. 1 3
      yudao-ui-admin-vue3/src/api/bpm/task/index.ts
  12. 1 2
      yudao-ui-admin-vue3/src/api/bpm/taskAssignRule/index.ts
  13. 1 2
      yudao-ui-admin-vue3/src/api/bpm/userGroup/index.ts
  14. 1 3
      yudao-ui-admin-vue3/src/api/infra/apiAccessLog/index.ts
  15. 1 3
      yudao-ui-admin-vue3/src/api/infra/apiErrorLog/index.ts
  16. 1 3
      yudao-ui-admin-vue3/src/api/infra/codegen/index.ts
  17. 1 3
      yudao-ui-admin-vue3/src/api/infra/config/index.ts
  18. 1 3
      yudao-ui-admin-vue3/src/api/infra/dataSourceConfig/index.ts
  19. 1 3
      yudao-ui-admin-vue3/src/api/infra/dbDoc/index.ts
  20. 1 3
      yudao-ui-admin-vue3/src/api/infra/fileConfig/index.ts
  21. 1 3
      yudao-ui-admin-vue3/src/api/infra/fileList/index.ts
  22. 1 3
      yudao-ui-admin-vue3/src/api/infra/job/index.ts
  23. 1 3
      yudao-ui-admin-vue3/src/api/infra/jobLog/index.ts
  24. 1 3
      yudao-ui-admin-vue3/src/api/infra/redis/index.ts
  25. 1 3
      yudao-ui-admin-vue3/src/api/login/index.ts
  26. 1 3
      yudao-ui-admin-vue3/src/api/pay/app/index.ts
  27. 1 3
      yudao-ui-admin-vue3/src/api/pay/channel/index.ts
  28. 1 3
      yudao-ui-admin-vue3/src/api/pay/merchant/index.ts
  29. 1 3
      yudao-ui-admin-vue3/src/api/pay/order/index.ts
  30. 1 3
      yudao-ui-admin-vue3/src/api/pay/refund/index.ts
  31. 1 3
      yudao-ui-admin-vue3/src/api/system/dept/index.ts
  32. 1 3
      yudao-ui-admin-vue3/src/api/system/dict/dict.data.ts
  33. 1 3
      yudao-ui-admin-vue3/src/api/system/dict/dict.type.ts
  34. 1 3
      yudao-ui-admin-vue3/src/api/system/errorCode/index.ts
  35. 1 3
      yudao-ui-admin-vue3/src/api/system/loginLog/index.ts
  36. 1 3
      yudao-ui-admin-vue3/src/api/system/menu/index.ts
  37. 1 3
      yudao-ui-admin-vue3/src/api/system/notice/index.ts
  38. 1 3
      yudao-ui-admin-vue3/src/api/system/oauth2/client.ts
  39. 1 3
      yudao-ui-admin-vue3/src/api/system/oauth2/token.ts
  40. 1 3
      yudao-ui-admin-vue3/src/api/system/operatelog/index.ts
  41. 1 3
      yudao-ui-admin-vue3/src/api/system/permission/index.ts
  42. 1 3
      yudao-ui-admin-vue3/src/api/system/post/index.ts
  43. 1 3
      yudao-ui-admin-vue3/src/api/system/role/index.ts
  44. 1 3
      yudao-ui-admin-vue3/src/api/system/sensitiveWord/index.ts
  45. 1 3
      yudao-ui-admin-vue3/src/api/system/sms/smsChannel/index.ts
  46. 1 3
      yudao-ui-admin-vue3/src/api/system/sms/smsLog/index.ts
  47. 1 3
      yudao-ui-admin-vue3/src/api/system/sms/smsTemplate/index.ts
  48. 1 3
      yudao-ui-admin-vue3/src/api/system/tenant/index.ts
  49. 1 3
      yudao-ui-admin-vue3/src/api/system/tenantPackage/index.ts
  50. 1 3
      yudao-ui-admin-vue3/src/api/system/user/index.ts
  51. 1 3
      yudao-ui-admin-vue3/src/api/system/user/profile/index.ts
  52. 3 0
      yudao-ui-admin-vue3/src/components/XButton/index.ts
  53. 37 0
      yudao-ui-admin-vue3/src/components/XButton/src/XButton.vue
  54. 3 0
      yudao-ui-admin-vue3/src/components/XModal/index.ts
  55. 42 0
      yudao-ui-admin-vue3/src/components/XModal/src/XModal.vue
  56. 4 0
      yudao-ui-admin-vue3/src/components/index.ts
  57. 39 222
      yudao-ui-admin-vue3/src/config/axios/index.ts
  58. 229 0
      yudao-ui-admin-vue3/src/config/axios/service.ts
  59. 0 60
      yudao-ui-admin-vue3/src/hooks/web/useAxios.ts
  60. 246 0
      yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts
  61. 60 0
      yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts
  62. 34 10
      yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts
  63. 1 1
      yudao-ui-admin-vue3/src/router/index.ts
  64. 6 0
      yudao-ui-admin-vue3/src/types/table.d.ts
  65. 308 307
      yudao-ui-admin-vue3/src/views/system/menu/index.vue
  66. 97 252
      yudao-ui-admin-vue3/src/views/system/post/index.vue
  67. 0 194
      yudao-ui-admin-vue3/src/views/system/post/indexd.vue
  68. 22 10
      yudao-ui-admin-vue3/src/views/system/post/post.data.ts

+ 2 - 2
yudao-dependencies/pom.xml

@@ -46,8 +46,8 @@
         <jasypt-spring-boot-starter.version>3.0.4</jasypt-spring-boot-starter.version>
         <lombok.version>1.18.24</lombok.version>
         <mapstruct.version>1.5.3.Final</mapstruct.version>
-        <hutool.version>5.8.8</hutool.version>
-        <easyexcel.verion>3.1.1</easyexcel.verion>
+        <hutool.version>5.8.9</hutool.version>
+        <easyexcel.verion>3.1.2</easyexcel.verion>
         <velocity.version>2.3</velocity.version>
         <screw.version>1.0.5</screw.version>
         <fastjson.version>1.2.83</fastjson.version>

+ 1 - 3
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { ${simpleClassName}VO, ${simpleClassName}PageReqVO, ${simpleClassName}ExcelReqVO } from './types'
 
-const request = useAxios()
-
 #set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}")
 // 查询${table.classComment}列表
 export const get${simpleClassName}PageApi = async (params: ${simpleClassName}PageReqVO) => {

+ 3 - 3
yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm

@@ -6,9 +6,9 @@ import { DICT_TYPE } from '@/utils/dict'
 import { useTable } from '@/hooks/web/useTable'
 import { useI18n } from '@/hooks/web/useI18n'
 import { FormExpose } from '@/components/Form'
-import type { ${simpleClassName}VO } from '@/api/${table.moduleName}/${simpleClassName}/types'
-import { rules, allSchemas } from './${simpleClassName}.data'
-import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${simpleClassName}'
+import type { ${simpleClassName}VO } from '@/api/${table.moduleName}/${classNameVar}/types'
+import { rules, allSchemas } from './${classNameVar}.data'
+import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}'
 const { t } = useI18n() // 国际化
 
 // ========== 列表相关 ==========

+ 1 - 1
yudao-ui-admin-vue3/.env

@@ -11,4 +11,4 @@ VITE_OPEN=true
 VITE_APP_TENANT_ENABLE=true
 
 # 验证码的开关
-VITE_APP_CAPTCHA_ENABLE=false
+VITE_APP_CAPTCHA_ENABLE=true

+ 7 - 7
yudao-ui-admin-vue3/package.json

@@ -36,9 +36,9 @@
     "dayjs": "^1.11.6",
     "echarts": "^5.4.0",
     "echarts-wordcloud": "^2.0.0",
-    "element-plus": "2.2.19",
+    "element-plus": "2.2.20",
     "intro.js": "^6.0.0",
-    "jsencrypt": "^3.3.0",
+    "jsencrypt": "^3.3.1",
     "js-cookie": "^3.0.1",
     "lodash-es": "^4.17.21",
     "mitt": "^3.0.0",
@@ -59,7 +59,7 @@
   "devDependencies": {
     "@commitlint/cli": "^17.2.0",
     "@commitlint/config-conventional": "^17.2.0",
-    "@iconify/json": "^2.1.131",
+    "@iconify/json": "^2.1.134",
     "@intlify/vite-plugin-vue-i18n": "^6.0.3",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.0",
@@ -73,7 +73,7 @@
     "@vitejs/plugin-vue": "^3.2.0",
     "@vitejs/plugin-vue-jsx": "^2.1.0",
     "autoprefixer": "^10.4.13",
-    "eslint": "^8.26.0",
+    "eslint": "^8.27.0",
     "eslint-config-prettier": "^8.5.0",
     "eslint-define-config": "^1.11.0",
     "eslint-plugin-prettier": "^4.2.1",
@@ -86,15 +86,15 @@
     "postcss-less": "^6.0.0",
     "prettier": "^2.7.1",
     "rimraf": "^3.0.2",
-    "rollup": "^2.79.1",
-    "stylelint": "^14.14.0",
+    "rollup": "^3.2.5",
+    "stylelint": "^14.14.1",
     "stylelint-config-html": "^1.1.0",
     "stylelint-config-prettier": "^9.0.3",
     "stylelint-config-recommended": "^9.0.0",
     "stylelint-config-standard": "^29.0.0",
     "stylelint-order": "^5.0.0",
     "typescript": "4.8.4",
-    "unplugin-vue-macros": "^0.15.2",
+    "unplugin-vue-macros": "^0.16.0",
     "vite": "3.2.2",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-eslint": "^1.8.1",

+ 162 - 134
yudao-ui-admin-vue3/pnpm-lock.yaml

@@ -4,7 +4,7 @@ specifiers:
   '@commitlint/cli': ^17.2.0
   '@commitlint/config-conventional': ^17.2.0
   '@iconify/iconify': ^3.0.0
-  '@iconify/json': ^2.1.131
+  '@iconify/json': ^2.1.134
   '@intlify/vite-plugin-vue-i18n': ^6.0.3
   '@purge-icons/generated': ^0.9.0
   '@types/intro.js': ^5.1.0
@@ -28,14 +28,15 @@ specifiers:
   dayjs: ^1.11.6
   echarts: ^5.4.0
   echarts-wordcloud: ^2.0.0
-  element-plus: 2.2.19
-  eslint: ^8.26.0
+  element-plus: 2.2.20
+  eslint: ^8.27.0
   eslint-config-prettier: ^8.5.0
   eslint-define-config: ^1.11.0
   eslint-plugin-prettier: ^4.2.1
   eslint-plugin-vue: ^9.7.0
   intro.js: ^6.0.0
-  jsencrypt: ^3.3.0
+  js-cookie: ^3.0.1
+  jsencrypt: ^3.3.1
   less: ^4.1.3
   lint-staged: ^13.0.3
   lodash-es: ^4.17.21
@@ -50,15 +51,15 @@ specifiers:
   qrcode: ^1.5.1
   qs: ^6.11.0
   rimraf: ^3.0.2
-  rollup: ^2.79.1
-  stylelint: ^14.14.0
+  rollup: ^3.2.5
+  stylelint: ^14.14.1
   stylelint-config-html: ^1.1.0
   stylelint-config-prettier: ^9.0.3
   stylelint-config-recommended: ^9.0.0
   stylelint-config-standard: ^29.0.0
   stylelint-order: ^5.0.0
   typescript: 4.8.4
-  unplugin-vue-macros: ^0.15.2
+  unplugin-vue-macros: ^0.16.0
   url: ^0.11.0
   vite: 3.2.2
   vite-plugin-compression: ^0.5.1
@@ -91,9 +92,10 @@ dependencies:
   dayjs: 1.11.6
   echarts: 5.4.0
   echarts-wordcloud: 2.0.0_echarts@5.4.0
-  element-plus: 2.2.19_vue@3.2.41
+  element-plus: 2.2.20_vue@3.2.41
   intro.js: 6.0.0
-  jsencrypt: 3.3.0
+  js-cookie: 3.0.1
+  jsencrypt: 3.3.1
   lodash-es: 4.17.21
   mitt: 3.0.0
   nprogress: 0.2.0
@@ -113,7 +115,7 @@ dependencies:
 devDependencies:
   '@commitlint/cli': 17.2.0
   '@commitlint/config-conventional': 17.2.0
-  '@iconify/json': 2.1.131
+  '@iconify/json': 2.1.134
   '@intlify/vite-plugin-vue-i18n': 6.0.3_vite@3.2.2+vue-i18n@9.2.2
   '@purge-icons/generated': 0.9.0
   '@types/intro.js': 5.1.0
@@ -122,16 +124,16 @@ devDependencies:
   '@types/nprogress': 0.2.0
   '@types/qrcode': 1.5.0
   '@types/qs': 6.9.7
-  '@typescript-eslint/eslint-plugin': 5.42.0_6xw5wg2354iw4zujk2f3vyfrzu
-  '@typescript-eslint/parser': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
+  '@typescript-eslint/eslint-plugin': 5.42.0_ofgjrzjuekeo7s3hdyz2yuzw34
+  '@typescript-eslint/parser': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
   '@vitejs/plugin-vue': 3.2.0_vite@3.2.2+vue@3.2.41
   '@vitejs/plugin-vue-jsx': 2.1.0_vite@3.2.2+vue@3.2.41
   autoprefixer: 10.4.13_postcss@8.4.18
-  eslint: 8.26.0
-  eslint-config-prettier: 8.5.0_eslint@8.26.0
+  eslint: 8.27.0
+  eslint-config-prettier: 8.5.0_eslint@8.27.0
   eslint-define-config: 1.11.0
-  eslint-plugin-prettier: 4.2.1_aniwkeyvlpmwkidetuytnokvcm
-  eslint-plugin-vue: 9.7.0_eslint@8.26.0
+  eslint-plugin-prettier: 4.2.1_v7o5sx5x3wbs57ifz6wc4f76we
+  eslint-plugin-vue: 9.7.0_eslint@8.27.0
   less: 4.1.3
   lint-staged: 13.0.3
   plop: 3.1.1
@@ -140,18 +142,18 @@ devDependencies:
   postcss-less: 6.0.0_postcss@8.4.18
   prettier: 2.7.1
   rimraf: 3.0.2
-  rollup: 2.79.1
-  stylelint: 14.14.0
-  stylelint-config-html: 1.1.0_2wb2ag3ubchp7fdo72hd6rkeve
-  stylelint-config-prettier: 9.0.3_stylelint@14.14.0
-  stylelint-config-recommended: 9.0.0_stylelint@14.14.0
-  stylelint-config-standard: 29.0.0_stylelint@14.14.0
-  stylelint-order: 5.0.0_stylelint@14.14.0
+  rollup: 3.2.5
+  stylelint: 14.14.1
+  stylelint-config-html: 1.1.0_a4i6jbpfaxelx4fvjhtlgvxx6i
+  stylelint-config-prettier: 9.0.3_stylelint@14.14.1
+  stylelint-config-recommended: 9.0.0_stylelint@14.14.1
+  stylelint-config-standard: 29.0.0_stylelint@14.14.1
+  stylelint-order: 5.0.0_stylelint@14.14.1
   typescript: 4.8.4
-  unplugin-vue-macros: 0.15.2_mbqnf624zbi2ta2juc2wzs2gvm
+  unplugin-vue-macros: 0.16.0_f6zolwli45a6dkgzq5zhbk2c7e
   vite: 3.2.2_less@4.1.3
   vite-plugin-compression: 0.5.1_vite@3.2.2
-  vite-plugin-eslint: 1.8.1_eslint@8.26.0+vite@3.2.2
+  vite-plugin-eslint: 1.8.1_eslint@8.27.0+vite@3.2.2
   vite-plugin-html: 3.2.0_vite@3.2.2
   vite-plugin-purge-icons: 0.9.1_vite@3.2.2
   vite-plugin-style-import: 2.0.0_vite@3.2.2
@@ -785,8 +787,8 @@ packages:
     dependencies:
       '@iconify/types': 2.0.0
 
-  /@iconify/json/2.1.131:
-    resolution: {integrity: sha512-VUQLJ2IHEcB0Is9K/grtLEC0OuLV3Fj0h3t04fKMN9hXwJkn2SKounQr44w7LzcBlu2RRYA7/npGlovtWV0iXQ==}
+  /@iconify/json/2.1.134:
+    resolution: {integrity: sha512-vqz3quli1lcFogDyu3H7mHyPjub+bZv8dJaR1lRk1gzblKBPlKoHhC0VaAjSMiA5L6toqXQRp7osjulB4JkA5Q==}
     dependencies:
       '@iconify/types': 2.0.0
       pathe: 0.3.9
@@ -1118,7 +1120,7 @@ packages:
   /@types/web-bluetooth/0.0.16:
     resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
 
-  /@typescript-eslint/eslint-plugin/5.42.0_6xw5wg2354iw4zujk2f3vyfrzu:
+  /@typescript-eslint/eslint-plugin/5.42.0_ofgjrzjuekeo7s3hdyz2yuzw34:
     resolution: {integrity: sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -1129,12 +1131,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
+      '@typescript-eslint/parser': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
       '@typescript-eslint/scope-manager': 5.42.0
-      '@typescript-eslint/type-utils': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
-      '@typescript-eslint/utils': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
+      '@typescript-eslint/type-utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
+      '@typescript-eslint/utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
       debug: 4.3.4
-      eslint: 8.26.0
+      eslint: 8.27.0
       ignore: 5.2.0
       natural-compare-lite: 1.4.0
       regexpp: 3.2.0
@@ -1145,7 +1147,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser/5.42.0_wyqvi574yv7oiwfeinomdzmc3m:
+  /@typescript-eslint/parser/5.42.0_rmayb2veg2btbq6mbmnyivgasy:
     resolution: {integrity: sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -1159,7 +1161,7 @@ packages:
       '@typescript-eslint/types': 5.42.0
       '@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4
       debug: 4.3.4
-      eslint: 8.26.0
+      eslint: 8.27.0
       typescript: 4.8.4
     transitivePeerDependencies:
       - supports-color
@@ -1173,7 +1175,7 @@ packages:
       '@typescript-eslint/visitor-keys': 5.42.0
     dev: true
 
-  /@typescript-eslint/type-utils/5.42.0_wyqvi574yv7oiwfeinomdzmc3m:
+  /@typescript-eslint/type-utils/5.42.0_rmayb2veg2btbq6mbmnyivgasy:
     resolution: {integrity: sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -1184,9 +1186,9 @@ packages:
         optional: true
     dependencies:
       '@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4
-      '@typescript-eslint/utils': 5.42.0_wyqvi574yv7oiwfeinomdzmc3m
+      '@typescript-eslint/utils': 5.42.0_rmayb2veg2btbq6mbmnyivgasy
       debug: 4.3.4
-      eslint: 8.26.0
+      eslint: 8.27.0
       tsutils: 3.21.0_typescript@4.8.4
       typescript: 4.8.4
     transitivePeerDependencies:
@@ -1219,7 +1221,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils/5.42.0_wyqvi574yv7oiwfeinomdzmc3m:
+  /@typescript-eslint/utils/5.42.0_rmayb2veg2btbq6mbmnyivgasy:
     resolution: {integrity: sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
@@ -1230,9 +1232,9 @@ packages:
       '@typescript-eslint/scope-manager': 5.42.0
       '@typescript-eslint/types': 5.42.0
       '@typescript-eslint/typescript-estree': 5.42.0_typescript@4.8.4
-      eslint: 8.26.0
+      eslint: 8.27.0
       eslint-scope: 5.1.1
-      eslint-utils: 3.0.0_eslint@8.26.0
+      eslint-utils: 3.0.0_eslint@8.27.0
       semver: 7.3.8
     transitivePeerDependencies:
       - supports-color
@@ -1355,26 +1357,26 @@ packages:
       '@volar/vue-language-core': 1.0.9
     dev: true
 
-  /@vue-macros/api/0.1.1:
-    resolution: {integrity: sha512-SZHwR1zsmgTi3HmqvcjUEiI+pdSLSOj1pW2g1rdXK8s2hpI9EdxPRPv+r3MSb+513rBZ8ZRsIs8HkDYszqi3vg==}
+  /@vue-macros/api/0.1.2:
+    resolution: {integrity: sha512-NuhWgOmxwmdHtGVhucLaRSJPymmr8Phabw2PuV8mLp7pRmjibp+99+RWF6IwXCNqVnEDYt3MwcEzTvPSzRcqjA==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@babel/types': 7.20.0
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
     dev: true
 
-  /@vue-macros/better-define/0.0.3:
-    resolution: {integrity: sha512-X3J6PEC2iNps1TCs1g/po15UMfW/6HG8VvRbDbGPCzjZ7MyQCYJE/aZ1vF5aqrU/+1xiGCCXkJv+5no6RAmq0Q==}
+  /@vue-macros/better-define/0.0.4:
+    resolution: {integrity: sha512-wp4C7Oom1P80oKvCtrpEDtotdIsTHXt0OqgBn47xUqdj4S/baG7Ji6qQ3MUtHxBTQ+nyC+7g9nxSADl+s1Px/Q==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/api': 0.1.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/api': 0.1.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
     dev: true
 
-  /@vue-macros/common/0.13.2:
-    resolution: {integrity: sha512-2pv9aUIw21IzdcROpo4FgMjEFR4zdV0ikHVF/kE/HX0xHcCteG4Pm7rHYK/JvIBhtiLDLWrcD31Y0B0XBaE0PQ==}
+  /@vue-macros/common/0.13.3:
+    resolution: {integrity: sha512-pV9UFwGZs7ddbAmJI5PZvNA2ZlNESQD81jOJM1wmwLnta66K8m0RQuJK2X/tdy7Avpm/p62M8opwbWyfXsBk4w==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@babel/types': 7.20.0
@@ -1382,8 +1384,8 @@ packages:
       magic-string: 0.26.7
     dev: true
 
-  /@vue-macros/define-model/0.13.7_@vueuse+core@9.4.0:
-    resolution: {integrity: sha512-nJXeeh94C49to6k6eGiSnaQGwWVqVpdLxHGFnJynzGCUIbaDAsrVfLWZWG1sbKNlGHHJRAFam4VqBavvqR7zAg==}
+  /@vue-macros/define-model/0.13.8_@vueuse+core@9.4.0:
+    resolution: {integrity: sha512-B/cV6n8wRRxwY4GLNo2q/YUzwYDR3Kbt2cI7Bxb51VRCxZexRqOazn5ZB8I9LdBwBLEenOX/3gRrHW3FApkRww==}
     engines: {node: '>=14.19.0'}
     peerDependencies:
       '@vueuse/core': ^9.0.0
@@ -1392,55 +1394,67 @@ packages:
         optional: true
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       '@vueuse/core': 9.4.0_vue@3.2.41
       ast-walker-scope: 0.3.0
       unplugin: 0.10.2
     dev: true
 
-  /@vue-macros/define-render/0.13.7_vue@3.2.41:
-    resolution: {integrity: sha512-7+9PDVVwKp9wtxFhsYDKIBTyr1qJYPOg5aMeG5btJ9zN2zVtNVVO+tx7yAE0xQBkHI7ml2bwy2PG3dFK/phmrQ==}
+  /@vue-macros/define-props/0.0.1_vue@3.2.41:
+    resolution: {integrity: sha512-LyB8k4JSwyBmjlVicxogD3N6+qGgpm7KJOeONNQJnHDo0bkdyssVXDIOEnqfgo+RdqJiqXKdFuBpw/lSvMp0KQ==}
+    engines: {node: '>=14.19.0'}
+    peerDependencies:
+      vue: ^3.2.25
+    dependencies:
+      '@rollup/pluginutils': 4.2.1
+      '@vue-macros/common': 0.13.3
+      unplugin: 0.10.2
+      vue: 3.2.41
+    dev: true
+
+  /@vue-macros/define-render/0.13.8_vue@3.2.41:
+    resolution: {integrity: sha512-a1FIGgTuvLN5gfbh7/dtYLSaXPBYLu33l2sc0auc55uNXqB6qXGzxu/12bEskONhYcx1BBwsqjWiSFbSJ3yLbg==}
     engines: {node: '>=14.19.0'}
     peerDependencies:
       vue: ^2.7.0 || ^3.0.0
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
       vue: 3.2.41
     dev: true
 
-  /@vue-macros/define-slots/0.0.4_vue@3.2.41:
-    resolution: {integrity: sha512-AAUz85J1ZL9Lsq3omhwkBe1p95L/VHixdcBonmt1jTCpjpCMmDWUmzl4iPh5pRMkk0D1lfvm5V/m0D68YqKZIg==}
+  /@vue-macros/define-slots/0.0.5_vue@3.2.41:
+    resolution: {integrity: sha512-+CS2MTH1fBeHbiZzThn6aXmrcbPRiuG29yWI6ugMarpIxwvVWXrPcvrWSE2BK/FSwcCR9Xfz7rETIo+BSjdo6A==}
     engines: {node: '>=14.19.0'}
     peerDependencies:
       vue: ^2.7.0 || ^3.0.0
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
       vue: 3.2.41
     dev: true
 
-  /@vue-macros/hoist-static/0.12.6:
-    resolution: {integrity: sha512-TeF6Dbug2PHaDuHd4VaiL+wPx58yJwaZyPhHA7IvGeo7xHh/nyXSj64aRMj3g5ovi38zXHHLvSb+yMNc3PYhBg==}
+  /@vue-macros/hoist-static/0.12.7:
+    resolution: {integrity: sha512-YRQ1zcy/sH13dgURk4PtazrLzMMnRDwg2N5xyeozaUWNczJmC65OWA5W9wK60WcxHu4pHRR/p7x+jive/bui9w==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
     dev: true
 
-  /@vue-macros/named-template/0.0.4_4bxfdhtj34q3f6vjmpq7dc2fyi:
-    resolution: {integrity: sha512-Hq2QSfglJD73boibJvKXUyiQyI/Gd5bSIF74mzY1Ec1CXhoUO4nupZE+0Ue/qk8Fq5XjUXYDGd2c/SCckBlK9w==}
+  /@vue-macros/named-template/0.0.5_kfit4shdbw4ah6piqfzpu53piy:
+    resolution: {integrity: sha512-bZRUljNyvOOqeE9dyqXvKPQCLUCcPt1EkThmXqSbxagV29ohyviF8+CCs/8OdmNygLTBIChjP8DexQ3nUIFzUg==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
       '@vitejs/plugin-vue': 3.2.0_vite@3.2.2+vue@3.2.41
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       '@vue/compiler-dom': 3.2.41
       unplugin: 0.10.2
-      unplugin-combine: 0.2.8_rollup@2.79.1+vite@3.2.2
+      unplugin-combine: 0.2.8_rollup@3.2.5+vite@3.2.2
     transitivePeerDependencies:
       - esbuild
       - rollup
@@ -1449,14 +1463,14 @@ packages:
       - webpack
     dev: true
 
-  /@vue-macros/setup-component/0.12.6_rollup@2.79.1+vite@3.2.2:
-    resolution: {integrity: sha512-oYknsBrC13HzZI9xvFz51KeQ8eOzCuki8wpT7BFOub33d9gJuvynpmqLl45FHSw2AJo8UA6vaKi6ef74JMukRg==}
+  /@vue-macros/setup-component/0.12.7_rollup@3.2.5+vite@3.2.2:
+    resolution: {integrity: sha512-L0WkJgw0QDwZh4tOjjKIDR0DMIybiOunsaxVqkJjicTb2YaiRUSLq4Wadl8Ttrsd0IEfI51CSlg7Sx0/dKLrlQ==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
-      unplugin-combine: 0.2.8_rollup@2.79.1+vite@3.2.2
+      unplugin-combine: 0.2.8_rollup@3.2.5+vite@3.2.2
     transitivePeerDependencies:
       - esbuild
       - rollup
@@ -1464,21 +1478,21 @@ packages:
       - webpack
     dev: true
 
-  /@vue-macros/setup-sfc/0.12.6:
-    resolution: {integrity: sha512-V7jXQ2sTTcxE4c+BTaWExd2kMi5w5qnJMjyzRPbQUTDf/KWNtPznTCxcmb9wVyJQRrrKfg61VYqpQ1jAtiDFoQ==}
+  /@vue-macros/setup-sfc/0.12.7:
+    resolution: {integrity: sha512-2RyeAEanl2MLeIoyfBKWSpJgXHjAazubF02hO7nbDCf2FgKqWB0Vu8NwyMZaQPNCWJuE4azLEodg8XBMJxyUKg==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
     dev: true
 
-  /@vue-macros/short-emits/0.12.7:
-    resolution: {integrity: sha512-CGy7Za/caP3WejNsHg4Si2bHeqxl2piv7Bf7TAMI/+948FuizQUFI8dRiv6dEQ8rGvegsU2BuTYH8JNDyzAK1A==}
+  /@vue-macros/short-emits/0.12.8:
+    resolution: {integrity: sha512-av/dqgPtU4EomWHHJPbGeuoYavCX2QTE0cd7Ka0dSuXuGgMhTN7feOv+sqsN8003yfOaPSBt6R+N/fy+T99GMA==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       unplugin: 0.10.2
     dev: true
 
@@ -2951,8 +2965,8 @@ packages:
     resolution: {integrity: sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ==}
     dev: true
 
-  /element-plus/2.2.19_vue@3.2.41:
-    resolution: {integrity: sha512-uN0gt9lUus/IHzu5J6vkbYoYJgUtU05osdtFv9RO27bHKOG5GN7dH6uA3OKfkQQ6R2sV8ZxY1rc9PH1X8Dgrow==}
+  /element-plus/2.2.20_vue@3.2.41:
+    resolution: {integrity: sha512-ludShd3f5kNRY4FLzeoNitLcwZ4qs2M/zwKeyeE7rUzZJAQ0BZtcT3SvZoEoBLmgxw9jHoonl4WIwon4UzhyRA==}
     peerDependencies:
       vue: ^3.2.0
     dependencies:
@@ -3278,13 +3292,13 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
-  /eslint-config-prettier/8.5.0_eslint@8.26.0:
+  /eslint-config-prettier/8.5.0_eslint@8.27.0:
     resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
     hasBin: true
     peerDependencies:
       eslint: '>=7.0.0'
     dependencies:
-      eslint: 8.26.0
+      eslint: 8.27.0
     dev: true
 
   /eslint-define-config/1.11.0:
@@ -3292,7 +3306,7 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0, npm: '>=6.14.13', pnpm: '>= 7.0.0'}
     dev: true
 
-  /eslint-plugin-prettier/4.2.1_aniwkeyvlpmwkidetuytnokvcm:
+  /eslint-plugin-prettier/4.2.1_v7o5sx5x3wbs57ifz6wc4f76we:
     resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
     engines: {node: '>=12.0.0'}
     peerDependencies:
@@ -3303,25 +3317,25 @@ packages:
       eslint-config-prettier:
         optional: true
     dependencies:
-      eslint: 8.26.0
-      eslint-config-prettier: 8.5.0_eslint@8.26.0
+      eslint: 8.27.0
+      eslint-config-prettier: 8.5.0_eslint@8.27.0
       prettier: 2.7.1
       prettier-linter-helpers: 1.0.0
     dev: true
 
-  /eslint-plugin-vue/9.7.0_eslint@8.26.0:
+  /eslint-plugin-vue/9.7.0_eslint@8.27.0:
     resolution: {integrity: sha512-DrOO3WZCZEwcLsnd3ohFwqCoipGRSTKTBTnLwdhqAbYZtzWl0o7D+D8ZhlmiZvABKTEl8AFsqH1GHGdybyoQmw==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
     dependencies:
-      eslint: 8.26.0
-      eslint-utils: 3.0.0_eslint@8.26.0
+      eslint: 8.27.0
+      eslint-utils: 3.0.0_eslint@8.27.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.10
       semver: 7.3.8
-      vue-eslint-parser: 9.1.0_eslint@8.26.0
+      vue-eslint-parser: 9.1.0_eslint@8.27.0
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -3350,13 +3364,13 @@ packages:
       eslint-visitor-keys: 1.3.0
     dev: true
 
-  /eslint-utils/3.0.0_eslint@8.26.0:
+  /eslint-utils/3.0.0_eslint@8.27.0:
     resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
     engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
     peerDependencies:
       eslint: '>=5'
     dependencies:
-      eslint: 8.26.0
+      eslint: 8.27.0
       eslint-visitor-keys: 2.1.0
     dev: true
 
@@ -3375,8 +3389,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /eslint/8.26.0:
-    resolution: {integrity: sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==}
+  /eslint/8.27.0:
+    resolution: {integrity: sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
@@ -3391,7 +3405,7 @@ packages:
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.1.1
-      eslint-utils: 3.0.0_eslint@8.26.0
+      eslint-utils: 3.0.0_eslint@8.27.0
       eslint-visitor-keys: 3.3.0
       espree: 9.4.0
       esquery: 1.4.0
@@ -4471,6 +4485,11 @@ packages:
     resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
     dev: true
 
+  /js-cookie/3.0.1:
+    resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==}
+    engines: {node: '>=12'}
+    dev: false
+
   /js-sdsl/4.1.5:
     resolution: {integrity: sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==}
     dev: true
@@ -4490,8 +4509,8 @@ packages:
       argparse: 2.0.1
     dev: true
 
-  /jsencrypt/3.3.0:
-    resolution: {integrity: sha512-OPfQMUU3pJ6KKTvKzr83jePHfyD1zWiWeLtRU9yIWjcbaRWm8MuXIu1/eQlDXPwDZrUjZy+9HVamS2Kmwrvz/Q==}
+  /jsencrypt/3.3.1:
+    resolution: {integrity: sha512-dVvV54GdFuJgmEKn+oBiaifDMen4p6o6j/lJh0OVMcouME8sST0bJ7bldIgKBQk4za0zyGn0/pm4vOznR25mLw==}
     dev: false
 
   /jsesc/2.5.2:
@@ -4577,8 +4596,8 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /known-css-properties/0.25.0:
-    resolution: {integrity: sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==}
+  /known-css-properties/0.26.0:
+    resolution: {integrity: sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==}
     dev: true
 
   /kolorist/1.6.0:
@@ -5940,6 +5959,14 @@ packages:
       fsevents: 2.3.2
     dev: true
 
+  /rollup/3.2.5:
+    resolution: {integrity: sha512-/Ha7HhVVofduy+RKWOQJrxe4Qb3xyZo+chcpYiD8SoQa4AG7llhupUtyfKSSrdBM2mWJjhM8wZwmbY23NmlIYw==}
+    engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+    hasBin: true
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
   /run-async/2.4.1:
     resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
     engines: {node: '>=0.12.0'}
@@ -6325,7 +6352,7 @@ packages:
     resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
     dev: true
 
-  /stylelint-config-html/1.1.0_2wb2ag3ubchp7fdo72hd6rkeve:
+  /stylelint-config-html/1.1.0_a4i6jbpfaxelx4fvjhtlgvxx6i:
     resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==}
     engines: {node: ^12 || >=14}
     peerDependencies:
@@ -6333,48 +6360,48 @@ packages:
       stylelint: '>=14.0.0'
     dependencies:
       postcss-html: 1.5.0
-      stylelint: 14.14.0
+      stylelint: 14.14.1
     dev: true
 
-  /stylelint-config-prettier/9.0.3_stylelint@14.14.0:
+  /stylelint-config-prettier/9.0.3_stylelint@14.14.1:
     resolution: {integrity: sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==}
     engines: {node: '>= 12'}
     hasBin: true
     peerDependencies:
       stylelint: '>=11.0.0'
     dependencies:
-      stylelint: 14.14.0
+      stylelint: 14.14.1
     dev: true
 
-  /stylelint-config-recommended/9.0.0_stylelint@14.14.0:
+  /stylelint-config-recommended/9.0.0_stylelint@14.14.1:
     resolution: {integrity: sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==}
     peerDependencies:
       stylelint: ^14.10.0
     dependencies:
-      stylelint: 14.14.0
+      stylelint: 14.14.1
     dev: true
 
-  /stylelint-config-standard/29.0.0_stylelint@14.14.0:
+  /stylelint-config-standard/29.0.0_stylelint@14.14.1:
     resolution: {integrity: sha512-uy8tZLbfq6ZrXy4JKu3W+7lYLgRQBxYTUUB88vPgQ+ZzAxdrvcaSUW9hOMNLYBnwH+9Kkj19M2DHdZ4gKwI7tg==}
     peerDependencies:
       stylelint: ^14.14.0
     dependencies:
-      stylelint: 14.14.0
-      stylelint-config-recommended: 9.0.0_stylelint@14.14.0
+      stylelint: 14.14.1
+      stylelint-config-recommended: 9.0.0_stylelint@14.14.1
     dev: true
 
-  /stylelint-order/5.0.0_stylelint@14.14.0:
+  /stylelint-order/5.0.0_stylelint@14.14.1:
     resolution: {integrity: sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw==}
     peerDependencies:
       stylelint: ^14.0.0
     dependencies:
       postcss: 8.4.18
       postcss-sorting: 7.0.1_postcss@8.4.18
-      stylelint: 14.14.0
+      stylelint: 14.14.1
     dev: true
 
-  /stylelint/14.14.0:
-    resolution: {integrity: sha512-yUI+4xXfPHVnueYddSQ/e1GuEA/2wVhWQbGj16AmWLtQJtn28lVxfS4b0CsWyVRPgd3Auzi0NXOthIEUhtQmmA==}
+  /stylelint/14.14.1:
+    resolution: {integrity: sha512-Jnftu+lSD8cSpcV/+Z2nfgfgFpTIS1FcujezXPngtoIQ6wtwutL22MsNE0dJuMiM1h1790g2qIjAyUZCMrX4sw==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
     hasBin: true
     dependencies:
@@ -6395,7 +6422,7 @@ packages:
       import-lazy: 4.0.0
       imurmurhash: 0.1.4
       is-plain-object: 5.0.0
-      known-css-properties: 0.25.0
+      known-css-properties: 0.26.0
       mathml-tag-names: 2.1.3
       meow: 9.0.0
       micromatch: 4.0.5
@@ -6413,7 +6440,7 @@ packages:
       style-search: 0.1.0
       supports-hyperlinks: 2.3.0
       svg-tags: 1.0.0
-      table: 6.8.0
+      table: 6.8.1
       v8-compile-cache: 2.3.0
       write-file-atomic: 4.0.2
     transitivePeerDependencies:
@@ -6497,8 +6524,8 @@ packages:
       stable: 0.1.8
     dev: true
 
-  /table/6.8.0:
-    resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==}
+  /table/6.8.1:
+    resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==}
     engines: {node: '>=10.0.0'}
     dependencies:
       ajv: 8.11.0
@@ -6730,7 +6757,7 @@ packages:
     engines: {node: '>= 10.0.0'}
     dev: true
 
-  /unplugin-combine/0.2.8_rollup@2.79.1+vite@3.2.2:
+  /unplugin-combine/0.2.8_rollup@3.2.5+vite@3.2.2:
     resolution: {integrity: sha512-Z38AC/TEjXbVyZ5HjVqo+lADj0/dcfwWC0Z4y0LNhybJzJQwmcMxm+ZsqHY3faauj4YigmlRMdptR5JEW9RuLg==}
     engines: {node: '>=14.19.0'}
     peerDependencies:
@@ -6749,40 +6776,41 @@ packages:
         optional: true
     dependencies:
       '@antfu/utils': 0.6.0
-      rollup: 2.79.1
+      rollup: 3.2.5
       unplugin: 0.10.2
       vite: 3.2.2_less@4.1.3
     dev: true
 
-  /unplugin-vue-define-options/0.12.6:
-    resolution: {integrity: sha512-uD4JTJVTDTRUTwLaT39VZkQ3gMlCba166oweU3jZJeIk8ku4Fh7N7UFazVzRch4THHFBVQ6dHvqvs4oPuCcJjg==}
+  /unplugin-vue-define-options/0.12.7:
+    resolution: {integrity: sha512-relFORVPLDs4dd3ogEti5YyAqQ62XEieRgLu/OrDIRZdSZQ942pydf9ilfLFiFwCUt+EES/2Xl4EERtgP1T/og==}
     engines: {node: '>=14.19.0'}
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/common': 0.13.2
+      '@vue-macros/common': 0.13.3
       ast-walker-scope: 0.3.0
       unplugin: 0.10.2
     dev: true
 
-  /unplugin-vue-macros/0.15.2_mbqnf624zbi2ta2juc2wzs2gvm:
-    resolution: {integrity: sha512-hFtUJRk9JE534S+mflAFh8QITOspACNCshVgn21NhdECRDAjyrp4xu+CzSIgKAob/xOgS7J2flLaOHSyqePSCQ==}
+  /unplugin-vue-macros/0.16.0_f6zolwli45a6dkgzq5zhbk2c7e:
+    resolution: {integrity: sha512-GdSJT+8Anc5zrGtZTvEnya2XVOneG0MEyyRrWWhVtn5xYEsUkrB0IxsHiT/mGjR5U+tjyQ/9sNL+FhhC54NFnQ==}
     engines: {node: '>=14.19.0'}
     peerDependencies:
       vue: ^2.7.0 || ^3.2.25
     dependencies:
       '@rollup/pluginutils': 4.2.1
-      '@vue-macros/better-define': 0.0.3
-      '@vue-macros/define-model': 0.13.7_@vueuse+core@9.4.0
-      '@vue-macros/define-render': 0.13.7_vue@3.2.41
-      '@vue-macros/define-slots': 0.0.4_vue@3.2.41
-      '@vue-macros/hoist-static': 0.12.6
-      '@vue-macros/named-template': 0.0.4_4bxfdhtj34q3f6vjmpq7dc2fyi
-      '@vue-macros/setup-component': 0.12.6_rollup@2.79.1+vite@3.2.2
-      '@vue-macros/setup-sfc': 0.12.6
-      '@vue-macros/short-emits': 0.12.7
+      '@vue-macros/better-define': 0.0.4
+      '@vue-macros/define-model': 0.13.8_@vueuse+core@9.4.0
+      '@vue-macros/define-props': 0.0.1_vue@3.2.41
+      '@vue-macros/define-render': 0.13.8_vue@3.2.41
+      '@vue-macros/define-slots': 0.0.5_vue@3.2.41
+      '@vue-macros/hoist-static': 0.12.7
+      '@vue-macros/named-template': 0.0.5_kfit4shdbw4ah6piqfzpu53piy
+      '@vue-macros/setup-component': 0.12.7_rollup@3.2.5+vite@3.2.2
+      '@vue-macros/setup-sfc': 0.12.7
+      '@vue-macros/short-emits': 0.12.8
       local-pkg: 0.4.2
-      unplugin-combine: 0.2.8_rollup@2.79.1+vite@3.2.2
-      unplugin-vue-define-options: 0.12.6
+      unplugin-combine: 0.2.8_rollup@3.2.5+vite@3.2.2
+      unplugin-vue-define-options: 0.12.7
       vue: 3.2.41
     transitivePeerDependencies:
       - '@vueuse/core'
@@ -6897,7 +6925,7 @@ packages:
       - supports-color
     dev: true
 
-  /vite-plugin-eslint/1.8.1_eslint@8.26.0+vite@3.2.2:
+  /vite-plugin-eslint/1.8.1_eslint@8.27.0+vite@3.2.2:
     resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==}
     peerDependencies:
       eslint: '>=7'
@@ -6905,7 +6933,7 @@ packages:
     dependencies:
       '@rollup/pluginutils': 4.2.1
       '@types/eslint': 8.4.6
-      eslint: 8.26.0
+      eslint: 8.27.0
       rollup: 2.79.1
       vite: 3.2.2_less@4.1.3
     dev: true
@@ -7041,14 +7069,14 @@ packages:
     dependencies:
       vue: 3.2.41
 
-  /vue-eslint-parser/9.1.0_eslint@8.26.0:
+  /vue-eslint-parser/9.1.0_eslint@8.27.0:
     resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: '>=6.0.0'
     dependencies:
       debug: 4.3.4
-      eslint: 8.26.0
+      eslint: 8.27.0
       eslint-scope: 7.1.1
       eslint-visitor-keys: 3.3.0
       espree: 9.4.0

+ 1 - 2
yudao-ui-admin-vue3/src/api/bpm/form/index.ts

@@ -1,6 +1,5 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { FormVO } from './types'
-const request = useAxios()
 
 // 创建工作流的表单定义
 export const createFormApi = async (data: FormVO) => {

+ 1 - 2
yudao-ui-admin-vue3/src/api/bpm/leave/index.ts

@@ -1,6 +1,5 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { LeaveVO } from './types'
-const request = useAxios()
 
 // 创建请假申请
 export const createLeaveApi = async (data: LeaveVO) => {

+ 1 - 2
yudao-ui-admin-vue3/src/api/bpm/model/index.ts

@@ -1,6 +1,5 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { ModelVO } from './types'
-const request = useAxios()
 
 export const getModelPageApi = async (params) => {
   return await request.get({ url: '/bpm/model/page', params })

+ 1 - 2
yudao-ui-admin-vue3/src/api/bpm/processInstance/index.ts

@@ -1,6 +1,5 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { ProcessInstanceVO } from './types'
-const request = useAxios()
 
 export const getMyProcessInstancePageApi = async (params) => {
   return await request.get({ url: '/bpm/process-instance/my-page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/bpm/task/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 export const getTodoTaskPage = async (params) => {
   return await request.get({ url: '/bpm/task/todo-page', params })

+ 1 - 2
yudao-ui-admin-vue3/src/api/bpm/taskAssignRule/index.ts

@@ -1,6 +1,5 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { TaskAssignVO } from './types'
-const request = useAxios()
 
 export const getTaskAssignRuleList = async (params) => {
   return await request.get({ url: '/bpm/task-assign-rule/list', params })

+ 1 - 2
yudao-ui-admin-vue3/src/api/bpm/userGroup/index.ts

@@ -1,6 +1,5 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { UserGroupVO } from './types'
-const request = useAxios()
 
 // 创建用户组
 export const createUserGroupApi = async (data: UserGroupVO) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/apiAccessLog/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询列表API 访问日志
 export const getApiAccessLogPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/apiErrorLog/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询列表API 访问日志
 export const getApiErrorLogPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/codegen/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { CodegenUpdateReqVO, CodegenCreateListReqVO } from './types'
 
-const request = useAxios()
-
 // 查询列表代码生成表定义
 export const getCodegenTablePageApi = (params) => {
   return request.get({ url: '/infra/codegen/table/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/config/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { ConfigVO } from './types'
 
-const request = useAxios()
-
 // 查询参数列表
 export const getConfigPageApi = (params) => {
   return request.get({ url: '/infra/config/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/dataSourceConfig/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { DataSourceConfigVO } from './types'
 
-const request = useAxios()
-
 // 查询数据源配置列表
 export const getDataSourceConfigListApi = () => {
   return request.get({ url: '/infra/data-source-config/list' })

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/dbDoc/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 导出Html
 export const exportHtmlApi = () => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/fileConfig/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { FileConfigVO } from './types'
 
-const request = useAxios()
-
 // 查询文件配置列表
 export const getFileConfigPageApi = (params) => {
   return request.get({ url: '/infra/file-config/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/fileList/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询文件列表
 export const getFilePageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/job/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { JobVO } from './types'
 
-const request = useAxios()
-
 // 任务列表
 export const getJobPageApi = (params) => {
   return request.get({ url: '/infra/job/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/jobLog/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 任务日志列表
 export const getJobLogPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/infra/redis/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 /**
  * 获取redis 监控信息

+ 1 - 3
yudao-ui-admin-vue3/src/api/login/index.ts

@@ -1,9 +1,7 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { getRefreshToken } from '@/utils/auth'
 import type { UserLoginVO } from './types'
 
-const request = useAxios()
-
 export interface CodeImgResult {
   captchaOnOff: boolean
   img: string

+ 1 - 3
yudao-ui-admin-vue3/src/api/pay/app/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { AppVO } from './types'
 
-const request = useAxios()
-
 // 查询列表支付应用
 export const getAppPageApi = (params) => {
   return request.get({ url: '/pay/app/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/pay/channel/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { ChannelVO } from './types'
 
-const request = useAxios()
-
 // 查询列表支付渠道
 export const getChannelPageApi = (params) => {
   return request.get({ url: '/pay/channel/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/pay/merchant/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { MerchantVO } from './types'
 
-const request = useAxios()
-
 // 查询列表支付商户
 export const getMerchantPageApi = (params) => {
   return request.get({ url: '/pay/merchant/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/pay/order/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { OrderVO } from './types'
 
-const request = useAxios()
-
 // 查询列表支付订单
 export const getOrderPageApi = async (params) => {
   return await request.get({ url: '/pay/order/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/pay/refund/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { RefundVO } from './types'
 
-const request = useAxios()
-
 // 查询列表退款订单
 export const getRefundPageApi = (params) => {
   return request.get({ url: '/pay/refund/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/dept/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { DeptVO, DeptListReqVO } from './types'
 
-const request = useAxios()
-
 // 查询部门(精简)列表
 export const listSimpleDeptApi = () => {
   return request.get({ url: '/system/dept/list-all-simple' })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/dict/dict.data.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { DictDataVO, DictDataPageReqVO, DictDataExportReqVO } from './types'
 
-const request = useAxios()
-
 // 查询字典数据(精简)列表
 export const listSimpleDictDataApi = () => {
   return request.get({ url: '/system/dict-data/list-all-simple' })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/dict/dict.type.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { DictTypeVO, DictTypePageReqVO, DictTypeExportReqVO } from './types'
 
-const request = useAxios()
-
 // 查询字典(精简)列表
 export const listSimpleDictTypeApi = () => {
   return request.get({ url: '/system/dict-type/list-all-simple' })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/errorCode/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { ErrorCodeVO } from './types'
 
-const request = useAxios()
-
 // 查询错误码列表
 export const getErrorCodePageApi = (params) => {
   return request.get({ url: '/system/error-code/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/loginLog/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询登录日志列表
 export const getLoginLogPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/menu/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { MenuVO } from './types'
 
-const request = useAxios()
-
 // 查询菜单(精简)列表
 export const listSimpleMenusApi = () => {
   return request.get({ url: '/system/menu/list-all-simple' })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/notice/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { NoticeVO } from './types'
 
-const request = useAxios()
-
 // 查询公告列表
 export const getNoticePageApi = (params) => {
   return request.get({ url: '/system/notice/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/oauth2/client.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import { OAuth2ClientVo } from './client.types'
 
-const request = useAxios()
-
 // 查询 OAuth2列表
 export const getOAuth2ClientPageApi = (params) => {
   return request.get({ url: '/system/oauth2-client/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/oauth2/token.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询 token列表
 export const getAccessTokenPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/operatelog/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询操作日志列表
 export const getOperateLogPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/permission/index.ts

@@ -1,12 +1,10 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type {
   PermissionAssignRoleDataScopeReqVO,
   PermissionAssignRoleMenuReqVO,
   PermissionAssignUserRoleReqVO
 } from './types'
 
-const request = useAxios()
-
 // 查询角色拥有的菜单权限
 export const listRoleMenusApi = async (roleId: number) => {
   return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/post/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { PostVO, PostPageReqVO, PostExportReqVO } from './types'
 
-const request = useAxios()
-
 // 查询岗位列表
 export const getPostPageApi = async (params: PostPageReqVO) => {
   return await request.get({ url: '/system/post/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/role/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { RoleVO } from './types'
 
-const request = useAxios()
-
 // 查询角色列表
 export const getRolePageApi = async (params) => {
   return await request.get({ url: '/system/role/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/sensitiveWord/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { SensitiveWordVO } from './types'
 
-const request = useAxios()
-
 // 查询敏感词列表
 export const getSensitiveWordPageApi = (params) => {
   return request.get({ url: '/system/sensitive-word/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/sms/smsChannel/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { SmsChannelVO } from './types'
 
-const request = useAxios()
-
 // 查询短信渠道列表
 export const getSmsChannelPageApi = (params) => {
   return request.get({ url: '/system/sms-channel/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/sms/smsLog/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询短信日志列表
 export const getSmsLogPageApi = (params) => {

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/sms/smsTemplate/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { SmsTemplateVO } from './types'
 
-const request = useAxios()
-
 // 查询短信模板列表
 export const getSmsTemplatePageApi = (params) => {
   return request.get({ url: '/system/sms-template/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/tenant/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { TenantVO } from './types'
 
-const request = useAxios()
-
 // 查询租户列表
 export const getTenantPageApi = (params) => {
   return request.get({ url: '/system/tenant/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/tenantPackage/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { TenantPackageVO } from './types'
 
-const request = useAxios()
-
 // 查询租户套餐列表
 export const getTenantPackageTypePageApi = (params) => {
   return request.get({ url: '/system/tenant-package/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/user/index.ts

@@ -1,8 +1,6 @@
-import { useAxios } from '@/hooks/web/useAxios'
+import request from '@/config/axios'
 import type { UserVO } from './types'
 
-const request = useAxios()
-
 // 查询用户管理列表
 export const getUserPageApi = (params) => {
   return request.get({ url: '/system/user/page', params })

+ 1 - 3
yudao-ui-admin-vue3/src/api/system/user/profile/index.ts

@@ -1,6 +1,4 @@
-import { useAxios } from '@/hooks/web/useAxios'
-
-const request = useAxios()
+import request from '@/config/axios'
 
 // 查询用户个人信息
 export const getUserProfileApi = () => {

+ 3 - 0
yudao-ui-admin-vue3/src/components/XButton/index.ts

@@ -0,0 +1,3 @@
+import XButton from './src/XButton.vue'
+
+export { XButton }

+ 37 - 0
yudao-ui-admin-vue3/src/components/XButton/src/XButton.vue

@@ -0,0 +1,37 @@
+<script setup lang="ts">
+import { propTypes } from '@/utils/propTypes'
+import { computed, useAttrs, PropType } from 'vue'
+
+const props = defineProps({
+  modelValue: propTypes.bool.def(false),
+  loading: propTypes.bool.def(false),
+  preIcon: propTypes.string.def(''),
+  postIcon: propTypes.string.def(''),
+  title: propTypes.string.def('按钮'),
+  type: propTypes.oneOf(['', 'primary', 'success', 'warning', 'danger', 'info']).def(''),
+  link: propTypes.bool.def(false),
+  circle: propTypes.bool.def(false),
+  round: propTypes.bool.def(false),
+  plain: propTypes.bool.def(false),
+  onClick: { type: Function as PropType<(...args) => any>, default: null }
+})
+const getBindValue = computed(() => {
+  const delArr: string[] = ['title', 'preIcon', 'postIcon', 'onClick']
+  const attrs = useAttrs()
+  const obj = { ...attrs, ...props }
+  for (const key in obj) {
+    if (delArr.indexOf(key) !== -1) {
+      delete obj[key]
+    }
+  }
+  return obj
+})
+</script>
+
+<template>
+  <el-button v-bind="getBindValue" @click="onClick">
+    <Icon :icon="preIcon" v-if="preIcon" class="mr-1px" />
+    {{ title }}
+    <Icon :icon="postIcon" v-if="postIcon" class="mr-1px" />
+  </el-button>
+</template>

+ 3 - 0
yudao-ui-admin-vue3/src/components/XModal/index.ts

@@ -0,0 +1,3 @@
+import XModal from './src/XModal.vue'
+
+export { XModal }

+ 42 - 0
yudao-ui-admin-vue3/src/components/XModal/src/XModal.vue

@@ -0,0 +1,42 @@
+<script setup lang="ts">
+import { propTypes } from '@/utils/propTypes'
+import { computed, useAttrs, useSlots } from 'vue'
+
+const slots = useSlots()
+
+const props = defineProps({
+  id: propTypes.string.def('model_1'),
+  modelValue: propTypes.bool.def(false),
+  fullscreen: propTypes.bool.def(false),
+  loading: propTypes.bool.def(false),
+  title: propTypes.string.def('弹窗'),
+  width: propTypes.string.def('800'),
+  height: propTypes.string.def('480'),
+  minWidth: propTypes.string.def('460'),
+  minHeight: propTypes.string.def('320'),
+  showFooter: propTypes.bool.def(true)
+})
+
+const getBindValue = computed(() => {
+  const attrs = useAttrs()
+  const obj = { ...attrs, ...props }
+  return obj
+})
+</script>
+
+<template>
+  <vxe-modal v-bind="getBindValue" destroy-on-close show-zoom resize transfer>
+    <template v-if="slots.header" #header>
+      <slot name="header"></slot>
+    </template>
+    <template v-if="slots.default" #default>
+      <slot name="default"></slot>
+    </template>
+    <template v-if="slots.corner" #corner>
+      <slot name="corner"></slot>
+    </template>
+    <template v-if="slots.footer" #footer>
+      <slot name="footer"></slot>
+    </template>
+  </vxe-modal>
+</template>

+ 4 - 0
yudao-ui-admin-vue3/src/components/index.ts

@@ -4,6 +4,8 @@ import { Form } from '@/components/Form'
 import { Table } from '@/components/Table'
 import { Search } from '@/components/Search'
 import { Dialog } from '@/components/Dialog'
+import { XModal } from '@/components/XModal'
+import { XButton } from '@/components/XButton'
 import { DictTag } from '@/components/DictTag'
 import { ContentWrap } from '@/components/ContentWrap'
 import { Descriptions } from '@/components/Descriptions'
@@ -14,6 +16,8 @@ export const setupGlobCom = (app: App<Element>): void => {
   app.component('Table', Table)
   app.component('Search', Search)
   app.component('Dialog', Dialog)
+  app.component('XModal', XModal)
+  app.component('XButton', XButton)
   app.component('DictTag', DictTag)
   app.component('ContentWrap', ContentWrap)
   app.component('Descriptions', Descriptions)

+ 39 - 222
yudao-ui-admin-vue3/src/config/axios/index.ts

@@ -1,229 +1,46 @@
-import axios, {
-  AxiosInstance,
-  AxiosRequestConfig,
-  AxiosRequestHeaders,
-  AxiosResponse,
-  AxiosError
-} from 'axios'
-import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
-import qs from 'qs'
-import { config } from '@/config/axios/config'
-import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
-import errorCode from './errorCode'
-import { useI18n } from '@/hooks/web/useI18n'
-import { resetRouter } from '@/router'
-import { useCache } from '@/hooks/web/useCache'
+import { service } from './service'
 
-const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
-const { result_code, base_url, request_timeout } = config
+import { config } from './config'
 
-// 需要忽略的提示。忽略后,自动 Promise.reject('error')
-const ignoreMsgs = [
-  '无效的刷新令牌', // 刷新令牌被删除时,不用提示
-  '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
-]
-// 是否显示重新登录
-export const isRelogin = { show: false }
-// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
-// 请求队列
-let requestList: any[] = []
-// 是否正在刷新中
-let isRefreshToken = false
+const { default_headers } = config
 
-// 创建axios实例
-const service: AxiosInstance = axios.create({
-  baseURL: base_url, // api 的 base_url
-  timeout: request_timeout, // 请求超时时间
-  withCredentials: false // 禁用 Cookie 等信息
-})
-
-// request拦截器
-service.interceptors.request.use(
-  (config: AxiosRequestConfig) => {
-    // 是否需要设置 token
-    const isToken = (config!.headers || {}).isToken === false
-    if (getAccessToken() && !isToken) {
-      ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
-    }
-    // 设置租户
-    if (tenantEnable) {
-      const tenantId = getTenantId()
-      if (tenantId) (config as Recordable).headers.common['tenant-id'] = tenantId
-    }
-    const params = config.params || {}
-    const data = config.data || false
-    if (
-      config.method?.toUpperCase() === 'POST' &&
-      (config.headers as AxiosRequestHeaders)['Content-Type'] ===
-        'application/x-www-form-urlencoded'
-    ) {
-      config.data = qs.stringify(data)
-    }
-    // get参数编码
-    if (config.method?.toUpperCase() === 'GET' && params) {
-      let url = config.url + '?'
-      for (const propName of Object.keys(params)) {
-        const value = params[propName]
-        if (value !== void 0 && value !== null && typeof value !== 'undefined') {
-          if (typeof value === 'object') {
-            for (const val of Object.keys(value)) {
-              const params = propName + '[' + val + ']'
-              const subPart = encodeURIComponent(params) + '='
-              url += subPart + encodeURIComponent(value[val]) + '&'
-            }
-          } else {
-            url += `${propName}=${encodeURIComponent(value)}&`
-          }
-        }
-      }
-      // 给 get 请求加上时间戳参数,避免从缓存中拿数据
-      // const now = new Date().getTime()
-      // params = params.substring(0, url.length - 1) + `?_t=${now}`
-      url = url.slice(0, -1)
-      config.params = {}
-      config.url = url
-    }
-    return config
+const request = (option: any) => {
+  const { url, method, params, data, headersType, responseType } = option
+  return service({
+    url: url,
+    method,
+    params,
+    data,
+    responseType: responseType,
+    headers: {
+      'Content-Type': headersType || default_headers
+    }
+  })
+}
+export default {
+  get: async <T = any>(option: any) => {
+    const res = await request({ method: 'GET', ...option })
+    return res.data as unknown as T
   },
-  (error: AxiosError) => {
-    // Do something with request error
-    console.log(error) // for debug
-    Promise.reject(error)
-  }
-)
-
-// response 拦截器
-service.interceptors.response.use(
-  async (response: AxiosResponse<Recordable>) => {
-    const { data } = response
-    const config = response.config
-    if (!data) {
-      // 返回“[HTTP]请求没有返回值”;
-      throw new Error()
-    }
-    const { t } = useI18n()
-    // 未设置状态码则默认成功状态
-    const code = data.code || result_code
-    // 二进制数据则直接返回
-    if (
-      response.request.responseType === 'blob' ||
-      response.request.responseType === 'arraybuffer'
-    ) {
-      return response.data
-    }
-    // 获取错误信息
-    const msg = data.msg || errorCode[code] || errorCode['default']
-    if (ignoreMsgs.indexOf(msg) !== -1) {
-      // 如果是忽略的错误码,直接返回 msg 异常
-      return Promise.reject(msg)
-    } else if (code === 401) {
-      // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
-      if (!isRefreshToken) {
-        isRefreshToken = true
-        // 1. 如果获取不到刷新令牌,则只能执行登出操作
-        if (!getRefreshToken()) {
-          return handleAuthorized()
-        }
-        // 2. 进行刷新访问令牌
-        try {
-          const refreshTokenRes = await refreshToken()
-          // 2.1 刷新成功,则回放队列的请求 + 当前请求
-          setToken(refreshTokenRes.data.data)
-          config.headers!.Authorization = 'Bearer ' + getAccessToken()
-          requestList.forEach((cb: any) => {
-            cb()
-          })
-          requestList = []
-          return service(config)
-        } catch (e) {
-          // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
-          // 2.2 刷新失败,只回放队列的请求
-          requestList.forEach((cb: any) => {
-            cb()
-          })
-          // 提示是否要登出。即不回放当前请求!不然会形成递归
-          return handleAuthorized()
-        } finally {
-          requestList = []
-          isRefreshToken = false
-        }
-      } else {
-        // 添加到队列,等待刷新获取到新的令牌
-        return new Promise((resolve) => {
-          requestList.push(() => {
-            config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
-            resolve(service(config))
-          })
-        })
-      }
-    } else if (code === 500) {
-      ElMessage.error(t('sys.api.errMsg500'))
-      return Promise.reject(new Error(msg))
-    } else if (code === 901) {
-      ElMessage.error(
-        '<div>' +
-          t('sys.api.errMsg901') +
-          '</div>' +
-          '<div> &nbsp; </div>' +
-          '<div>参考 https://doc.iocoder.cn/ 教程</div>' +
-          '<div> &nbsp; </div>' +
-          '<div>5 分钟搭建本地环境</div>'
-      )
-      return Promise.reject(new Error(msg))
-    } else if (code !== 200) {
-      if (msg === '无效的刷新令牌') {
-        // hard coding:忽略这个提示,直接登出
-        console.log(msg)
-      } else {
-        ElNotification.error({
-          title: msg
-        })
-      }
-      return Promise.reject('error')
-    } else {
-      return data
-    }
+  post: async <T = any>(option: any) => {
+    const res = await request({ method: 'POST', ...option })
+    return res.data as unknown as T
   },
-  (error: AxiosError) => {
-    console.log('err' + error) // for debug
-    let { message } = error
-    const { t } = useI18n()
-    if (message === 'Network Error') {
-      message = t('sys.api.errorMessage')
-    } else if (message.includes('timeout')) {
-      message = t('sys.api.apiTimeoutMessage')
-    } else if (message.includes('Request failed with status code')) {
-      message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
-    }
-    ElMessage.error(message)
-    return Promise.reject(error)
-  }
-)
-
-const refreshToken = async () => {
-  return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
-}
-const handleAuthorized = () => {
-  const { t } = useI18n()
-  if (!isRelogin.show) {
-    isRelogin.show = true
-    ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
-      confirmButtonText: t('login.relogin'),
-      cancelButtonText: t('common.cancel'),
-      type: 'warning'
-    })
-      .then(() => {
-        const { wsCache } = useCache()
-        resetRouter() // 重置静态路由表
-        wsCache.clear()
-        removeToken()
-        isRelogin.show = false
-        window.location.href = '/'
-      })
-      .catch(() => {
-        isRelogin.show = false
-      })
+  delete: async <T = any>(option: any) => {
+    const res = await request({ method: 'DELETE', ...option })
+    return res.data as unknown as T
+  },
+  put: async <T = any>(option: any) => {
+    const res = await request({ method: 'PUT', ...option })
+    return res.data as unknown as T
+  },
+  download: async <T = any>(option: any) => {
+    const res = await request({ method: 'GET', responseType: 'blob', ...option })
+    return res as unknown as Promise<T>
+  },
+  upload: async <T = any>(option: any) => {
+    option.headersType = 'multipart/form-data'
+    const res = await request({ method: 'PUT', ...option })
+    return res as unknown as Promise<T>
   }
-  return Promise.reject(t('sys.api.timeoutMessage'))
 }
-export { service }

+ 229 - 0
yudao-ui-admin-vue3/src/config/axios/service.ts

@@ -0,0 +1,229 @@
+import axios, {
+  AxiosInstance,
+  AxiosRequestConfig,
+  AxiosRequestHeaders,
+  AxiosResponse,
+  AxiosError
+} from 'axios'
+import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
+import qs from 'qs'
+import { config } from '@/config/axios/config'
+import { getAccessToken, getRefreshToken, getTenantId, removeToken, setToken } from '@/utils/auth'
+import errorCode from './errorCode'
+import { useI18n } from '@/hooks/web/useI18n'
+import { resetRouter } from '@/router'
+import { useCache } from '@/hooks/web/useCache'
+
+const tenantEnable = import.meta.env.VITE_APP_TENANT_ENABLE
+const { result_code, base_url, request_timeout } = config
+
+// 需要忽略的提示。忽略后,自动 Promise.reject('error')
+const ignoreMsgs = [
+  '无效的刷新令牌', // 刷新令牌被删除时,不用提示
+  '刷新令牌已过期' // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
+]
+// 是否显示重新登录
+export const isRelogin = { show: false }
+// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
+// 请求队列
+let requestList: any[] = []
+// 是否正在刷新中
+let isRefreshToken = false
+
+// 创建axios实例
+const service: AxiosInstance = axios.create({
+  baseURL: base_url, // api 的 base_url
+  timeout: request_timeout, // 请求超时时间
+  withCredentials: false // 禁用 Cookie 等信息
+})
+
+// request拦截器
+service.interceptors.request.use(
+  (config: AxiosRequestConfig) => {
+    // 是否需要设置 token
+    const isToken = (config!.headers || {}).isToken === false
+    if (getAccessToken() && !isToken) {
+      ;(config as Recordable).headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
+    }
+    // 设置租户
+    if (tenantEnable) {
+      const tenantId = getTenantId()
+      if (tenantId) (config as Recordable).headers.common['tenant-id'] = tenantId
+    }
+    const params = config.params || {}
+    const data = config.data || false
+    if (
+      config.method?.toUpperCase() === 'POST' &&
+      (config.headers as AxiosRequestHeaders)['Content-Type'] ===
+        'application/x-www-form-urlencoded'
+    ) {
+      config.data = qs.stringify(data)
+    }
+    // get参数编码
+    if (config.method?.toUpperCase() === 'GET' && params) {
+      let url = config.url + '?'
+      for (const propName of Object.keys(params)) {
+        const value = params[propName]
+        if (value !== void 0 && value !== null && typeof value !== 'undefined') {
+          if (typeof value === 'object') {
+            for (const val of Object.keys(value)) {
+              const params = propName + '[' + val + ']'
+              const subPart = encodeURIComponent(params) + '='
+              url += subPart + encodeURIComponent(value[val]) + '&'
+            }
+          } else {
+            url += `${propName}=${encodeURIComponent(value)}&`
+          }
+        }
+      }
+      // 给 get 请求加上时间戳参数,避免从缓存中拿数据
+      // const now = new Date().getTime()
+      // params = params.substring(0, url.length - 1) + `?_t=${now}`
+      url = url.slice(0, -1)
+      config.params = {}
+      config.url = url
+    }
+    return config
+  },
+  (error: AxiosError) => {
+    // Do something with request error
+    console.log(error) // for debug
+    Promise.reject(error)
+  }
+)
+
+// response 拦截器
+service.interceptors.response.use(
+  async (response: AxiosResponse<Recordable>) => {
+    const { data } = response
+    const config = response.config
+    if (!data) {
+      // 返回“[HTTP]请求没有返回值”;
+      throw new Error()
+    }
+    const { t } = useI18n()
+    // 未设置状态码则默认成功状态
+    const code = data.code || result_code
+    // 二进制数据则直接返回
+    if (
+      response.request.responseType === 'blob' ||
+      response.request.responseType === 'arraybuffer'
+    ) {
+      return response.data
+    }
+    // 获取错误信息
+    const msg = data.msg || errorCode[code] || errorCode['default']
+    if (ignoreMsgs.indexOf(msg) !== -1) {
+      // 如果是忽略的错误码,直接返回 msg 异常
+      return Promise.reject(msg)
+    } else if (code === 401) {
+      // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
+      if (!isRefreshToken) {
+        isRefreshToken = true
+        // 1. 如果获取不到刷新令牌,则只能执行登出操作
+        if (!getRefreshToken()) {
+          return handleAuthorized()
+        }
+        // 2. 进行刷新访问令牌
+        try {
+          const refreshTokenRes = await refreshToken()
+          // 2.1 刷新成功,则回放队列的请求 + 当前请求
+          setToken(refreshTokenRes.data.data)
+          config.headers!.Authorization = 'Bearer ' + getAccessToken()
+          requestList.forEach((cb: any) => {
+            cb()
+          })
+          requestList = []
+          return service(config)
+        } catch (e) {
+          // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
+          // 2.2 刷新失败,只回放队列的请求
+          requestList.forEach((cb: any) => {
+            cb()
+          })
+          // 提示是否要登出。即不回放当前请求!不然会形成递归
+          return handleAuthorized()
+        } finally {
+          requestList = []
+          isRefreshToken = false
+        }
+      } else {
+        // 添加到队列,等待刷新获取到新的令牌
+        return new Promise((resolve) => {
+          requestList.push(() => {
+            config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+            resolve(service(config))
+          })
+        })
+      }
+    } else if (code === 500) {
+      ElMessage.error(t('sys.api.errMsg500'))
+      return Promise.reject(new Error(msg))
+    } else if (code === 901) {
+      ElMessage.error(
+        '<div>' +
+          t('sys.api.errMsg901') +
+          '</div>' +
+          '<div> &nbsp; </div>' +
+          '<div>参考 https://doc.iocoder.cn/ 教程</div>' +
+          '<div> &nbsp; </div>' +
+          '<div>5 分钟搭建本地环境</div>'
+      )
+      return Promise.reject(new Error(msg))
+    } else if (code !== 200) {
+      if (msg === '无效的刷新令牌') {
+        // hard coding:忽略这个提示,直接登出
+        console.log(msg)
+      } else {
+        ElNotification.error({
+          title: msg
+        })
+      }
+      return Promise.reject('error')
+    } else {
+      return data
+    }
+  },
+  (error: AxiosError) => {
+    console.log('err' + error) // for debug
+    let { message } = error
+    const { t } = useI18n()
+    if (message === 'Network Error') {
+      message = t('sys.api.errorMessage')
+    } else if (message.includes('timeout')) {
+      message = t('sys.api.apiTimeoutMessage')
+    } else if (message.includes('Request failed with status code')) {
+      message = t('sys.api.apiRequestFailed') + message.substr(message.length - 3)
+    }
+    ElMessage.error(message)
+    return Promise.reject(error)
+  }
+)
+
+const refreshToken = async () => {
+  return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + getRefreshToken())
+}
+const handleAuthorized = () => {
+  const { t } = useI18n()
+  if (!isRelogin.show) {
+    isRelogin.show = true
+    ElMessageBox.confirm(t('sys.api.timeoutMessage'), t('common.confirmTitle'), {
+      confirmButtonText: t('login.relogin'),
+      cancelButtonText: t('common.cancel'),
+      type: 'warning'
+    })
+      .then(() => {
+        const { wsCache } = useCache()
+        resetRouter() // 重置静态路由表
+        wsCache.clear()
+        removeToken()
+        isRelogin.show = false
+        window.location.href = '/'
+      })
+      .catch(() => {
+        isRelogin.show = false
+      })
+  }
+  return Promise.reject(t('sys.api.timeoutMessage'))
+}
+export { service }

+ 0 - 60
yudao-ui-admin-vue3/src/hooks/web/useAxios.ts

@@ -1,60 +0,0 @@
-import { service } from '@/config/axios'
-
-import { config } from '@/config/axios/config'
-
-const { default_headers } = config
-
-const request = (option: AxiosConfig) => {
-  const { url, method, params, data, headersType, responseType } = option
-  return service({
-    url: url,
-    method,
-    params,
-    data,
-    responseType: responseType,
-    headers: {
-      'Content-Type': headersType || default_headers
-    }
-  })
-}
-
-async function getFn<T = any>(option: AxiosConfig): Promise<T> {
-  const res = await request({ method: 'GET', ...option })
-  return res.data
-}
-
-async function postFn<T = any>(option: AxiosConfig): Promise<T> {
-  const res = await request({ method: 'POST', ...option })
-  return res.data
-}
-
-async function deleteFn<T = any>(option: AxiosConfig): Promise<T> {
-  const res = await request({ method: 'DELETE', ...option })
-  return res.data
-}
-
-async function putFn<T = any>(option: AxiosConfig): Promise<T> {
-  const res = await request({ method: 'PUT', ...option })
-  return res.data
-}
-async function downloadFn<T = any>(option: AxiosConfig): Promise<T> {
-  const res = await request({ method: 'GET', responseType: 'blob', ...option })
-  return res as unknown as Promise<T>
-}
-
-async function uploadFn<T = any>(option: AxiosConfig): Promise<T> {
-  option.headersType = 'multipart/form-data'
-  const res = await request({ method: 'PUT', ...option })
-  return res as unknown as Promise<T>
-}
-
-export const useAxios = () => {
-  return {
-    get: getFn,
-    post: postFn,
-    delete: deleteFn,
-    put: putFn,
-    download: downloadFn,
-    upload: uploadFn
-  }
-}

+ 246 - 0
yudao-ui-admin-vue3/src/hooks/web/useVxeCrudSchemas.ts

@@ -0,0 +1,246 @@
+import { DescriptionsSchema } from '@/types/descriptions'
+import { getIntDictOptions } from '@/utils/dict'
+import { reactive } from 'vue'
+import {
+  FormItemRenderOptions,
+  VxeFormItemProps,
+  VxeGridPropTypes,
+  VxeTableDefines
+} from 'vxe-table'
+import { eachTree } from 'xe-utils'
+import { useI18n } from '@/hooks/web/useI18n'
+import { VxeTableColumn } from '@/types/table'
+
+export type VxeCrudSchema = Omit<VxeTableColumn, 'children'> & {
+  field: string
+  title?: string
+  search?: CrudSearchParams
+  table?: CrudTableParams
+  form?: CrudFormParams
+  detail?: CrudDescriptionsParams
+  print?: boolean
+  children?: VxeCrudSchema[]
+  dictType?: string
+}
+type CrudSearchParams = {
+  // 是否显示在查询项
+  show?: boolean
+} & Omit<VxeFormItemProps, 'field'>
+
+type CrudTableParams = {
+  // 是否显示表头
+  show?: boolean
+} & Omit<VxeTableDefines.ColumnOptions, 'field'>
+
+type CrudFormParams = {
+  // 是否显示表单项
+  show?: boolean
+} & Omit<VxeFormItemProps, 'field'>
+
+type CrudDescriptionsParams = {
+  // 是否显示表单项
+  show?: boolean
+} & Omit<DescriptionsSchema, 'field'>
+
+interface VxeAllSchemas {
+  searchSchema: VxeFormItemProps[]
+  tableSchema: VxeGridPropTypes.Columns
+  formSchema: VxeFormItemProps[]
+  detailSchema: DescriptionsSchema[]
+  printSchema: VxeTableDefines.ColumnInfo[]
+}
+
+// 过滤所有结构
+export const useVxeCrudSchemas = (
+  crudSchema: VxeCrudSchema[]
+): {
+  allSchemas: VxeAllSchemas
+} => {
+  // 所有结构数据
+  const allSchemas = reactive<VxeAllSchemas>({
+    searchSchema: [],
+    tableSchema: [],
+    formSchema: [],
+    detailSchema: [],
+    printSchema: []
+  })
+
+  const searchSchema = filterSearchSchema(crudSchema)
+  allSchemas.searchSchema = searchSchema || []
+
+  const tableSchema = filterTableSchema(crudSchema)
+  allSchemas.tableSchema = tableSchema || []
+
+  const formSchema = filterFormSchema(crudSchema)
+  allSchemas.formSchema = formSchema
+
+  const detailSchema = filterDescriptionsSchema(crudSchema)
+  allSchemas.detailSchema = detailSchema
+
+  const printSchema = filterPrintSchema(crudSchema)
+  allSchemas.printSchema = printSchema
+
+  return {
+    allSchemas
+  }
+}
+
+// 过滤 Search 结构
+const filterSearchSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => {
+  const searchSchema: VxeFormItemProps[] = []
+  const { t } = useI18n()
+  eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+    // 判断是否显示
+    if (schemaItem?.search?.show) {
+      let itemRenderName = schemaItem?.search?.itemRender?.name || '$input'
+      const options: any[] = []
+      let itemRender: FormItemRenderOptions = {
+        name: itemRenderName,
+        props: { placeholder: t('common.inputText') }
+      }
+      if (schemaItem.dictType) {
+        const allOptions = { label: '全部', value: '' }
+        options.push(allOptions)
+        getIntDictOptions(schemaItem.dictType).forEach((dict) => {
+          options.push(dict)
+        })
+        itemRender.options = options
+        if (!schemaItem.search.itemRender?.name) itemRenderName = '$select'
+        itemRender = {
+          name: itemRenderName,
+          options: options,
+          props: { placeholder: t('common.selectText') }
+        }
+      }
+
+      const searchSchemaItem = {
+        // 默认为 input
+        span: 6,
+        itemRender: itemRender,
+        ...schemaItem.search,
+        field: schemaItem.field,
+        title: schemaItem.search?.title || schemaItem.title
+      }
+      // 删除不必要的字段
+      delete searchSchemaItem.show
+
+      searchSchema.push(searchSchemaItem)
+    }
+  })
+  // 添加搜索按钮
+  const buttons: VxeFormItemProps = {
+    span: 24,
+    align: 'center',
+    collapseNode: true,
+    itemRender: {
+      name: '$buttons',
+      children: [
+        { props: { type: 'submit', content: t('common.query'), status: 'primary' } },
+        { props: { type: 'reset', content: t('common.reset') } }
+      ]
+    }
+  }
+  searchSchema.push(buttons)
+  return searchSchema
+}
+
+// 过滤 table 结构
+const filterTableSchema = (crudSchema: VxeCrudSchema[]): VxeGridPropTypes.Columns => {
+  const tableSchema: VxeGridPropTypes.Columns = []
+  eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+    // 判断是否显示
+    if (schemaItem?.table?.show !== false) {
+      const tableSchemaItem = {
+        ...schemaItem.table,
+        field: schemaItem.field,
+        title: schemaItem.table?.title || schemaItem.title
+      }
+
+      // 删除不必要的字段
+      delete tableSchemaItem.show
+
+      tableSchema.push(tableSchemaItem)
+    }
+  })
+  return tableSchema
+}
+
+// 过滤 form 结构
+const filterFormSchema = (crudSchema: VxeCrudSchema[]): VxeFormItemProps[] => {
+  const formSchema: VxeFormItemProps[] = []
+  const { t } = useI18n()
+  eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+    // 判断是否显示
+    if (schemaItem?.form?.show !== false) {
+      let itemRenderName = schemaItem?.form?.itemRender?.name || '$input'
+      let itemRender: FormItemRenderOptions = {
+        name: itemRenderName,
+        props: { placeholder: t('common.inputText') }
+      }
+      if (schemaItem.dictType) {
+        if (!(schemaItem.form && schemaItem.form.itemRender?.name)) itemRenderName = '$select'
+        itemRender = {
+          name: itemRenderName,
+          options: getIntDictOptions(schemaItem.dictType),
+          props: { placeholder: t('common.selectText') }
+        }
+      }
+      const formSchemaItem = {
+        // 默认为 input
+        itemRender: itemRender,
+        ...schemaItem.form,
+        span: schemaItem.form?.span || 12,
+        field: schemaItem.field,
+        title: schemaItem.form?.title || schemaItem.title
+      }
+
+      // 删除不必要的字段
+      delete formSchemaItem.show
+
+      formSchema.push(formSchemaItem)
+    }
+  })
+
+  return formSchema
+}
+
+// 过滤 descriptions 结构
+const filterDescriptionsSchema = (crudSchema: VxeCrudSchema[]): DescriptionsSchema[] => {
+  const descriptionsSchema: DescriptionsSchema[] = []
+
+  eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+    // 判断是否显示
+    if (schemaItem?.detail?.show !== false) {
+      const descriptionsSchemaItem = {
+        ...schemaItem.detail,
+        field: schemaItem.field,
+        label: schemaItem.detail?.label || schemaItem.title
+      }
+
+      // 删除不必要的字段
+      delete descriptionsSchemaItem.show
+
+      descriptionsSchema.push(descriptionsSchemaItem)
+    }
+  })
+
+  return descriptionsSchema
+}
+
+// 过滤 打印 结构
+const filterPrintSchema = (crudSchema: VxeCrudSchema[]): any[] => {
+  const printSchema: any[] = []
+
+  eachTree(crudSchema, (schemaItem: VxeCrudSchema) => {
+    // 判断是否显示
+    if (schemaItem?.detail?.show !== false) {
+      const printSchemaItem = {
+        field: schemaItem.field
+      }
+
+      printSchema.push(printSchemaItem)
+    }
+  })
+
+  return printSchema
+}

+ 60 - 0
yudao-ui-admin-vue3/src/hooks/web/useVxeGrid.ts

@@ -0,0 +1,60 @@
+import { reactive } from 'vue'
+import { VxeGridProps } from 'vxe-table'
+
+export const useVxeGrid = (allSchemas, getPageApi) => {
+  const gridOptions = reactive<VxeGridProps>({
+    loading: false,
+    height: 800,
+    rowConfig: {
+      keyField: 'id',
+      isHover: true
+    },
+    toolbarConfig: {
+      custom: true,
+      slots: { buttons: 'toolbar_buttons' }
+    },
+    printConfig: {
+      columns: allSchemas.printSchema
+    },
+    formConfig: {
+      titleWidth: 100,
+      titleAlign: 'right',
+      items: allSchemas.searchSchema
+    },
+    columns: allSchemas.tableSchema,
+    pagerConfig: {
+      border: false,
+      background: false,
+      perfect: true,
+      pageSize: 10,
+      pagerCount: 7,
+      pageSizes: [5, 10, 15, 20, 50, 100, 200, 500],
+      layouts: [
+        'PrevJump',
+        'PrevPage',
+        'Jump',
+        'PageCount',
+        'NextPage',
+        'NextJump',
+        'Sizes',
+        'Total'
+      ]
+    },
+    proxyConfig: {
+      seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号)
+      form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为
+      props: { result: 'list', total: 'total' },
+      ajax: {
+        query: ({ page, form }) => {
+          const queryParams = Object.assign({}, form)
+          queryParams.pageSize = page.pageSize
+          queryParams.pageNo = page.currentPage
+          return new Promise(async (resolve) => {
+            resolve(await getPageApi(queryParams))
+          })
+        }
+      }
+    }
+  })
+  return gridOptions
+}

+ 34 - 10
yudao-ui-admin-vue3/src/plugins/vxeTable/index.ts

@@ -60,7 +60,14 @@ VXETable.setup({
     autoResize: true, // 自动监听父元素的变化去重新计算表格
     resizable: true, // 列是否允许拖动列宽调整大小
     emptyText: '暂无数据', // 空表单
-    highlightHoverRow: true // 自动监听父元素的变化去重新计算表格
+    highlightHoverRow: true, // 自动监听父元素的变化去重新计算表格
+    treeConfig: {
+      rowField: 'id',
+      parentField: 'parentId',
+      children: 'children',
+      indent: 20,
+      showIcon: true
+    }
   },
   grid: {
     toolbarConfig: {
@@ -107,8 +114,10 @@ VXETable.setup({
     titleColon: true // 是否显示标题冒号
   },
   modal: {
-    width: 600, // 窗口的宽度
-    height: 400, // 窗口的高度
+    width: 800, // 窗口的宽度
+    height: 600, // 窗口的高度
+    minWidth: 460,
+    minHeight: 320,
     showZoom: true, // 标题是否标显示最大化与还原按钮
     resize: true, // 是否允许窗口边缘拖动调整窗口大小
     marginSize: 0, // 只对 resize 启用后有效,用于设置可拖动界限范围,如果为负数则允许拖动超出屏幕边界
@@ -127,13 +136,28 @@ VXETable.setup({
       : XEUtils.toFormatString(XEUtils.get(enUS, key), args)
   }
 })
-// 格式金额,默认2位数
-VXETable.formats.add('formatAmount', ({ cellValue }, digits = 2) => {
-  return XEUtils.commafy(XEUtils.toNumber(cellValue), { digits })
-})
-// 格式日期,默认 yyyy-MM-dd HH:mm:ss
-VXETable.formats.add('formatDate', ({ cellValue }, format = 'yyyy-MM-dd HH:mm:ss') => {
-  return XEUtils.toDateString(cellValue, format)
+// 自定义全局的格式化处理函数
+VXETable.formats.mixin({
+  // 格式日期,默认 yyyy-MM-dd HH:mm:ss
+  formatDate({ cellValue }, format) {
+    return XEUtils.toDateString(cellValue, format || 'yyyy-MM-dd HH:mm:ss')
+  },
+  // 四舍五入金额,每隔3位逗号分隔,默认2位数
+  formatAmount({ cellValue }, digits = 2) {
+    return XEUtils.commafy(Number(cellValue), { digits })
+  },
+  // 格式化银行卡,默认每4位空格隔开
+  formatBankcard({ cellValue }) {
+    return XEUtils.commafy(XEUtils.toValueString(cellValue), { spaceNumber: 4, separator: ' ' })
+  },
+  // 四舍五入,默认两位数
+  formatFixedNumber({ cellValue }, digits = 2) {
+    return XEUtils.toFixed(XEUtils.round(cellValue, digits), digits)
+  },
+  // 向下舍入,默认两位数
+  formatCutNumber({ cellValue }, digits = 2) {
+    return XEUtils.toFixed(XEUtils.floor(cellValue, digits), digits)
+  }
 })
 export const setupVxeTable = (app: App<Element>) => {
   // 表格功能

+ 1 - 1
yudao-ui-admin-vue3/src/router/index.ts

@@ -10,7 +10,7 @@ import { usePermissionStoreWithOut } from '@/store/modules/permission'
 import { useDictStoreWithOut } from '@/store/modules/dict'
 import { useUserStoreWithOut } from '@/store/modules/user'
 import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
-import { isRelogin } from '@/config/axios'
+import { isRelogin } from '@/config/axios/service'
 import { getInfoApi } from '@/api/login'
 
 const { start, done } = useNProgress()

+ 6 - 0
yudao-ui-admin-vue3/src/types/table.d.ts

@@ -4,6 +4,12 @@ export type TableColumn = {
   children?: TableColumn[]
 } & Recordable
 
+export type VxeTableColumn = {
+  field: string
+  title?: string
+  children?: TableColumn[]
+} & Recordable
+
 export type TableSlotDefault = {
   row: Recordable
   column: TableColumn

+ 308 - 307
yudao-ui-admin-vue3/src/views/system/menu/index.vue

@@ -1,16 +1,262 @@
+<template>
+  <ContentWrap>
+    <el-form :model="queryParams" ref="queryForm" :inline="true">
+      <el-form-item label="菜单名称" prop="name">
+        <el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择菜单状态">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <XButton
+          type="primary"
+          preIcon="ep:search"
+          :title="t('common.query')"
+          @click="handleQuery()"
+        />
+        <XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" />
+      </el-form-item>
+    </el-form>
+    <vxe-toolbar>
+      <template #buttons>
+        <XButton
+          type="primary"
+          preIcon="ep:zoom-in"
+          :title="t('action.add')"
+          v-hasPermi="['system:menu:create']"
+          @click="handleCreate()"
+        />
+        <XButton title="展开所有" @click="xTable?.setAllTreeExpand(true)" />
+        <XButton title="关闭所有" @click="xTable?.clearTreeExpand()" />
+      </template>
+    </vxe-toolbar>
+    <vxe-table
+      show-overflow
+      keep-source
+      ref="xTable"
+      :loading="tableLoading"
+      :row-config="{ keyField: 'id' }"
+      :column-config="{ resizable: true }"
+      :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
+      :print-config="{}"
+      :export-config="{}"
+      :data="tableData"
+      class="xtable"
+    >
+      <vxe-column title="菜单名称" field="name" width="200" tree-node>
+        <template #default="{ row }">
+          <Icon :icon="row.icon" />
+          <span class="ml-3">{{ row.name }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column title="菜单类型" field="type">
+        <template #default="{ row }">
+          <DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="row.type" />
+        </template>
+      </vxe-column>
+      <vxe-column title="路由地址" field="path" />
+      <vxe-column title="组件路径" field="component" />
+      <vxe-column title="权限标识" field="permission" />
+      <vxe-column title="排序" field="sort" />
+      <vxe-column title="状态" field="status">
+        <template #default="{ row }">
+          <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
+        </template>
+      </vxe-column>
+      <vxe-column title="创建时间" field="createTime" formatter="formatDate" />
+      <vxe-column title="操作" width="200">
+        <template #default="{ row }">
+          <XButton
+            link
+            type="primary"
+            preIcon="ep:edit"
+            :title="t('action.edit')"
+            v-hasPermi="['system:menu:update']"
+            @click="handleUpdate(row.id)"
+          />
+          <XButton
+            link
+            type="primary"
+            preIcon="ep:delete"
+            :title="t('action.del')"
+            v-hasPermi="['system:menu:delete']"
+            @click="handleDelete(row)"
+          />
+        </template>
+      </vxe-column>
+    </vxe-table>
+  </ContentWrap>
+  <XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle">
+    <template #default>
+      <!-- 对话框(添加 / 修改) -->
+      <el-form
+        :model="menuForm"
+        :rules="rules"
+        :inline="true"
+        label-width="120px"
+        label-position="right"
+      >
+        <el-row :gutter="24">
+          <el-col :span="24">
+            <el-form-item label="上级菜单">
+              <el-tree-select
+                node-key="id"
+                v-model="menuForm.parentId"
+                :props="menuProps"
+                :data="menuOptions"
+                check-strictly
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="菜单类型" prop="type">
+              <el-radio-group v-model="menuForm.type">
+                <el-radio-button
+                  v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
+                  :key="dict.value"
+                  :label="dict.value"
+                >
+                  {{ dict.label }}
+                </el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="菜单名称" prop="name">
+              <el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
+            </el-form-item>
+          </el-col>
+          <template v-if="menuForm.type !== 3">
+            <el-col :span="12">
+              <el-form-item label="菜单图标">
+                <IconSelect v-model="menuForm.icon" clearable />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="路由地址" prop="path">
+                <template #label>
+                  <Tooltip
+                    titel="路由地址"
+                    message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
+                  />
+                </template>
+                <el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
+              </el-form-item>
+            </el-col>
+          </template>
+          <template v-if="menuForm.type === 2">
+            <el-col :span="12">
+              <el-form-item label="路由地址" prop="component">
+                <el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
+              </el-form-item>
+            </el-col>
+          </template>
+          <template v-if="menuForm.type !== 1">
+            <el-col :span="12">
+              <el-form-item label="权限标识" prop="permission">
+                <template #label>
+                  <Tooltip
+                    titel="权限标识"
+                    message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
+                  />
+                </template>
+                <el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
+              </el-form-item>
+            </el-col>
+          </template>
+          <el-col :span="12">
+            <el-form-item label="显示排序" prop="sort">
+              <el-input-number
+                v-model="menuForm.sort"
+                controls-position="right"
+                :min="0"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="菜单状态" prop="status">
+              <el-radio-group v-model="menuForm.status">
+                <el-radio-button
+                  v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+                  :key="dict.value"
+                  :label="dict.value"
+                >
+                  {{ dict.label }}
+                </el-radio-button>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <template v-if="menuForm.type !== 3">
+            <el-col :span="12">
+              <el-form-item label="显示状态" prop="status">
+                <template #label>
+                  <Tooltip
+                    titel="显示状态"
+                    message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
+                  />
+                </template>
+                <el-radio-group v-model="menuForm.visible">
+                  <el-radio-button key="true" :label="true">显示</el-radio-button>
+                  <el-radio-button key="false" :label="false">隐藏</el-radio-button>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+          </template>
+          <template v-if="menuForm.type === 2">
+            <el-col :span="12">
+              <el-form-item label="缓存状态" prop="keepAlive">
+                <template #label>
+                  <Tooltip
+                    titel="缓存状态"
+                    message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
+                  />
+                </template>
+                <el-radio-group v-model="menuForm.keepAlive">
+                  <el-radio-button key="true" :label="true">缓存</el-radio-button>
+                  <el-radio-button key="false" :label="false">不缓存</el-radio-button>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+          </template>
+        </el-row>
+      </el-form>
+    </template>
+    <template #footer>
+      <!-- 操作按钮 -->
+      <XButton
+        v-if="['create', 'update'].includes(actionType)"
+        type="primary"
+        :loading="actionLoading"
+        @click="submitForm"
+        :title="t('action.save')"
+      />
+      <XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" />
+    </template>
+  </XModal>
+</template>
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue'
-import { handleTree } from '@/utils/tree'
-import dayjs from 'dayjs'
-import { IconSelect } from '@/components/Icon'
-import { Tooltip } from '@/components/Tooltip'
 import * as MenuApi from '@/api/system/menu'
+import { MenuVO } from '@/api/system/menu/types'
 import { useI18n } from '@/hooks/web/useI18n'
+import { useMessage } from '@/hooks/web/useMessage'
+import { IconSelect } from '@/components/Icon'
+import { Tooltip } from '@/components/Tooltip'
+import { required } from '@/utils/formRules.js'
+import { onMounted, reactive, ref } from 'vue'
+import { VxeTableInstance } from 'vxe-table'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
 import {
   ElRow,
   ElCol,
-  ElTable,
-  ElTableColumn,
   ElForm,
   ElFormItem,
   ElInput,
@@ -21,21 +267,32 @@ import {
   ElRadioGroup,
   ElRadioButton
 } from 'element-plus'
-import { MenuVO } from '@/api/system/menu/types'
-import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { useMessage } from '@/hooks/web/useMessage'
-import { required } from '@/utils/formRules.js'
-const message = useMessage()
+import { handleTree } from '@/utils/tree'
 const { t } = useI18n() // 国际化
-// ========== 创建菜单树结构 ==========
-const loading = ref(true)
-const menuData = ref<any[]>([]) // 树形结构
-const getList = async () => {
-  const res = await MenuApi.getMenuListApi(queryParams)
-  menuData.value = handleTree(res)
-  loading.value = false
-}
+const message = useMessage()
+const xTable = ref<VxeTableInstance>()
+const tableLoading = ref(false)
+const tableData = ref()
+const actionLoading = ref(false) // 遮罩层
+const actionType = ref('') // 操作按钮的类型
+const dialogVisible = ref(false) // 是否显示弹出层
+const dialogTitle = ref('edit') // 弹出层标题
+const statusOption = ref() // 状态选项
+const menuForm = ref<MenuVO>({
+  id: 0,
+  name: '',
+  permission: '',
+  type: SystemMenuTypeEnum.DIR,
+  sort: 1,
+  parentId: 0,
+  path: '',
+  icon: '',
+  component: '',
+  status: CommonStatusEnum.ENABLE,
+  visible: true,
+  keepAlive: true,
+  createTime: ''
+})
 const menuProps = {
   checkStrictly: true,
   children: 'children',
@@ -57,46 +314,16 @@ const getTree = async () => {
 }
 // ========== 查询 ==========
 const queryParams = reactive({
-  name: undefined,
-  status: undefined
+  name: null,
+  status: null
 })
-// 查询操作
-const handleQuery = async () => {
-  await getList()
-}
-// 重置操作
-const resetQuery = async () => {
-  queryParams.name = undefined
-  queryParams.status = undefined
-  await getList()
+const getList = async () => {
+  statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS)
+  tableLoading.value = true
+  const res = await MenuApi.getMenuListApi(queryParams)
+  tableData.value = res
+  tableLoading.value = false
 }
-// ========== CRUD 相关 ==========
-const actionLoading = ref(false) // 遮罩层
-const actionType = ref('') // 操作按钮的类型
-const dialogVisible = ref(false) // 是否显示弹出层
-const dialogTitle = ref('edit') // 弹出层标题
-const menuForm = ref<MenuVO>({
-  id: 0,
-  name: '',
-  permission: '',
-  type: SystemMenuTypeEnum.DIR,
-  sort: 1,
-  parentId: 0,
-  path: '',
-  icon: '',
-  component: '',
-  status: CommonStatusEnum.ENABLE,
-  visible: true,
-  keepAlive: true,
-  createTime: ''
-})
-// 表单校验
-const rules = reactive({
-  name: [required],
-  sort: [required],
-  path: [required],
-  status: [required]
-})
 // 设置标题
 const setDialogTile = (type: string) => {
   dialogTitle.value = t('action.' + type)
@@ -105,25 +332,39 @@ const setDialogTile = (type: string) => {
 }
 // 新建操作
 const handleCreate = () => {
-  // 重置表单
   setDialogTile('create')
 }
 // 修改操作
 const handleUpdate = async (row: MenuVO) => {
   // 设置数据
   const res = await MenuApi.getMenuApi(row.id)
+  console.log(res)
   menuForm.value = res
   setDialogTile('update')
 }
 // 删除操作
 const handleDelete = async (row: MenuVO) => {
-  message
-    .confirm(t('common.delDataMessage'), t('common.confirmTitle'))
-    .then(async () => {
-      await MenuApi.deleteMenuApi(row.id)
-      message.success(t('common.delSuccess'))
-    })
-    .catch(() => {})
+  message.confirm(t('common.delDataMessage'), t('common.confirmTitle')).then(async () => {
+    await MenuApi.deleteMenuApi(row.id)
+    message.success(t('common.delSuccess'))
+    await getList()
+  })
+}
+// 表单校验
+const rules = reactive({
+  name: [required],
+  sort: [required],
+  path: [required],
+  status: [required]
+})
+// 查询操作
+const handleQuery = async () => {
+  await getList()
+}
+// 重置操作
+const resetQuery = async () => {
+  queryParams.name = null
+  queryParams.status = null
   await getList()
 }
 // 保存操作
@@ -162,248 +403,8 @@ const submitForm = async () => {
     actionLoading.value = false
   }
 }
-// ========== 初始化 ==========
 onMounted(async () => {
   await getList()
   getTree()
 })
 </script>
-<template>
-  <!-- 搜索工作区 -->
-  <ContentWrap>
-    <el-form :model="queryParams" ref="queryForm" :inline="true">
-      <el-form-item label="菜单名称" prop="name">
-        <el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
-      </el-form-item>
-      <el-form-item label="状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="请选择菜单状态">
-          <el-option
-            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" @click="handleQuery">
-          <Icon icon="ep:search" class="mr-5px" />
-          {{ t('common.query') }}
-        </el-button>
-        <el-button @click="resetQuery">
-          <Icon icon="ep:refresh-right" class="mr-5px" />
-          {{ t('common.reset') }}
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
-  <!-- 列表 -->
-  <ContentWrap>
-    <div class="mb-10px">
-      <el-button type="primary" v-hasPermi="['system:notice:create']" @click="handleCreate">
-        <Icon icon="ep:zoom-in" class="mr-1px" /> {{ t('action.add') }}
-      </el-button>
-    </div>
-    <el-table
-      v-loading="loading"
-      table-layout="auto"
-      row-key="id"
-      :data="menuData"
-      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
-    >
-      <el-table-column label="菜单名称" prop="name" width="240px">
-        <template #default="scope">
-          <Icon :icon="scope.row.icon" />
-          <span class="ml-3">{{ scope.row.name }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="菜单类型" prop="type">
-        <template #default="scope">
-          <DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="scope.row.type" />
-        </template>
-      </el-table-column>
-      <el-table-column label="路由地址" prop="path" />
-      <el-table-column label="组件路径" prop="component" />
-      <el-table-column label="权限标识" prop="permission" />
-      <el-table-column label="排序" prop="sort" />
-      <el-table-column label="状态" prop="status">
-        <template #default="scope">
-          <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
-        </template>
-      </el-table-column>
-      <el-table-column label="创建时间" prop="createTime">
-        <template #default="scope">
-          <span>{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            v-hasPermi="['system:menu:update']"
-            @click="handleUpdate(scope.row)"
-          >
-            <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
-          </el-button>
-          <el-button
-            link
-            type="primary"
-            v-hasPermi="['system:menu:delete']"
-            @click="handleDelete(scope.row)"
-          >
-            <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-  </ContentWrap>
-  <!-- 添加或修改菜单对话框 -->
-  <Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="400px" width="45%">
-    <el-form
-      :model="menuForm"
-      :rules="rules"
-      :inline="true"
-      label-width="120px"
-      label-position="right"
-    >
-      <el-row :gutter="24">
-        <el-col :span="24">
-          <el-form-item label="上级菜单">
-            <el-tree-select
-              node-key="id"
-              v-model="menuForm.parentId"
-              :props="menuProps"
-              :data="menuOptions"
-              check-strictly
-            />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="菜单类型" prop="type">
-            <el-radio-group v-model="menuForm.type">
-              <el-radio-button
-                v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
-                :key="dict.value"
-                :label="dict.value"
-              >
-                {{ dict.label }}
-              </el-radio-button>
-            </el-radio-group>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="菜单名称" prop="name">
-            <el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
-          </el-form-item>
-        </el-col>
-        <template v-if="menuForm.type !== 3">
-          <el-col :span="12">
-            <el-form-item label="菜单图标">
-              <IconSelect v-model="menuForm.icon" clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="路由地址" prop="path">
-              <template #label>
-                <Tooltip
-                  titel="路由地址"
-                  message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
-                />
-              </template>
-              <el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
-            </el-form-item>
-          </el-col>
-        </template>
-        <template v-if="menuForm.type === 2">
-          <el-col :span="12">
-            <el-form-item label="路由地址" prop="component">
-              <el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
-            </el-form-item>
-          </el-col>
-        </template>
-        <template v-if="menuForm.type !== 1">
-          <el-col :span="12">
-            <el-form-item label="权限标识" prop="permission">
-              <template #label>
-                <Tooltip
-                  titel="权限标识"
-                  message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
-                />
-              </template>
-              <el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
-            </el-form-item>
-          </el-col>
-        </template>
-        <el-col :span="12">
-          <el-form-item label="显示排序" prop="sort">
-            <el-input-number v-model="menuForm.sort" controls-position="right" :min="0" clearable />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="菜单状态" prop="status">
-            <el-radio-group v-model="menuForm.status">
-              <el-radio-button
-                v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-                :key="dict.value"
-                :label="dict.value"
-              >
-                {{ dict.label }}
-              </el-radio-button>
-            </el-radio-group>
-          </el-form-item>
-        </el-col>
-        <template v-if="menuForm.type !== 3">
-          <el-col :span="12">
-            <el-form-item label="显示状态" prop="status">
-              <template #label>
-                <Tooltip
-                  titel="显示状态"
-                  message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
-                />
-              </template>
-              <el-radio-group v-model="menuForm.visible">
-                <el-radio-button key="true" :label="true">显示</el-radio-button>
-                <el-radio-button key="false" :label="false">隐藏</el-radio-button>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </template>
-        <template v-if="menuForm.type === 2">
-          <el-col :span="12">
-            <el-form-item label="缓存状态" prop="keepAlive">
-              <template #label>
-                <Tooltip
-                  titel="缓存状态"
-                  message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
-                />
-              </template>
-              <el-radio-group v-model="menuForm.keepAlive">
-                <el-radio-button key="true" :label="true">缓存</el-radio-button>
-                <el-radio-button key="false" :label="false">不缓存</el-radio-button>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </template>
-      </el-row>
-    </el-form>
-    <!-- 操作按钮 -->
-    <template #footer>
-      <el-button
-        v-if="['create', 'update'].includes(actionType)"
-        type="primary"
-        :loading="actionLoading"
-        @click="submitForm"
-      >
-        {{ t('action.save') }}
-      </el-button>
-      <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
-    </template>
-  </Dialog>
-</template>
-<style lang="less" scoped>
-:deep(.el-button.is-text) {
-  margin-left: 0;
-  padding: 8px 10px;
-}
-</style>

+ 97 - 252
yudao-ui-admin-vue3/src/views/system/post/index.vue

@@ -1,24 +1,100 @@
+<template>
+  <ContentWrap>
+    <vxe-grid ref="xGrid" v-bind="gridOptions" class="xtable-scrollbar">
+      <template #toolbar_buttons>
+        <XButton
+          type="primary"
+          preIcon="ep:zoom-in"
+          :title="t('action.add')"
+          v-hasPermi="['system:post:create']"
+          @click="handleCreate()"
+        />
+      </template>
+      <template #status_default="{ row }">
+        <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
+      </template>
+      <template #action_default="{ row }">
+        <XButton
+          link
+          type="primary"
+          preIcon="ep:edit"
+          :title="t('action.edit')"
+          v-hasPermi="['system:post:update']"
+          @click="handleUpdate(row.id)"
+        />
+        <XButton
+          link
+          type="primary"
+          preIcon="ep:view"
+          :title="t('action.detail')"
+          v-hasPermi="['system:post:update']"
+          @click="handleDetail(row)"
+        />
+        <XButton
+          link
+          type="primary"
+          preIcon="ep:delete"
+          :title="t('action.del')"
+          v-hasPermi="['system:post:delete']"
+          @click="handleDelete(row.id)"
+        />
+      </template>
+    </vxe-grid>
+  </ContentWrap>
+  <XModal id="postModel" v-model="dialogVisible" :title="dialogTitle">
+    <template #default>
+      <!-- 对话框(添加 / 修改) -->
+      <vxe-form
+        ref="xForm"
+        v-if="['create', 'update'].includes(actionType)"
+        :data="formData"
+        :items="formItems"
+        :rules="rules"
+      />
+      <Descriptions
+        v-if="actionType === 'detail'"
+        :schema="allSchemas.detailSchema"
+        :data="detailRef"
+      >
+        <template #status="{ row }">
+          <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
+        </template>
+        <template #createTime="{ row }">
+          <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
+        </template>
+      </Descriptions>
+    </template>
+    <template #footer>
+      <XButton
+        v-if="['create', 'update'].includes(actionType)"
+        :loading="actionLoading"
+        :title="t('action.save')"
+        @click="submitForm"
+      />
+      <XButton
+        v-if="['create', 'update'].includes(actionType)"
+        :loading="actionLoading"
+        :title="t('dialog.close')"
+        @click="dialogVisible = false"
+      />
+    </template>
+  </XModal>
+</template>
 <script setup lang="ts">
-import { reactive, ref } from 'vue'
+import { ref } from 'vue'
 import dayjs from 'dayjs'
 import { useI18n } from '@/hooks/web/useI18n'
-import {
-  VxeFormEvents,
-  VxeFormInstance,
-  VxeFormItemProps,
-  VxeGrid,
-  VxeGridInstance,
-  VxeGridProps
-} from 'vxe-table'
+import { VxeFormEvents, VxeFormInstance, VxeFormItemProps, VxeGridInstance } from 'vxe-table'
 import * as PostApi from '@/api/system/post'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 import { ContentWrap } from '@/components/ContentWrap'
-import { PostPageReqVO, PostVO } from '@/api/system/post/types'
+import { PostVO } from '@/api/system/post/types'
 import { rules, allSchemas } from './post.data'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import { useMessage } from '@/hooks/web/useMessage'
+import { useVxeGrid } from '@/hooks/web/useVxeGrid'
 
 const { t } = useI18n() // 国际化
-
+const message = useMessage()
 const xGrid = ref<VxeGridInstance>()
 const xForm = ref<VxeFormInstance>()
 const dialogVisible = ref(false) // 是否显示弹出层
@@ -26,158 +102,9 @@ const dialogTitle = ref('edit') // 弹出层标题
 const actionType = ref('') // 操作按钮的类型
 const actionLoading = ref(false) // 遮罩层
 
-const gridOptions = reactive<VxeGridProps>({
-  loading: false,
-  rowConfig: {
-    keyField: 'id',
-    isHover: true
-  },
-  toolbarConfig: {
-    custom: true,
-    slots: { buttons: 'toolbar_buttons' }
-  },
-  printConfig: {
-    columns: [
-      { field: 'name' },
-      { field: 'code' },
-      { field: 'sort' },
-      { field: 'status' },
-      { field: 'createTime' }
-    ]
-  },
-  formConfig: {
-    titleWidth: 100,
-    titleAlign: 'right',
-    items: [
-      {
-        field: 'name',
-        title: '岗位名称',
-        span: 6,
-        itemRender: { name: '$input', props: { placeholder: '请输入岗位名称' } }
-      },
-      {
-        field: 'code',
-        title: '岗位编码',
-        span: 6,
-        itemRender: { name: '$input', props: { placeholder: '请输入岗位编码' } }
-      },
-      {
-        field: 'status',
-        title: t('common.status'),
-        span: 6,
-        itemRender: { name: '$select', options: getIntDictOptions(DICT_TYPE.COMMON_STATUS) }
-      },
-      {
-        span: 24,
-        align: 'center',
-        collapseNode: true,
-        itemRender: {
-          name: '$buttons',
-          children: [
-            { props: { type: 'submit', content: t('common.query'), status: 'primary' } },
-            { props: { type: 'reset', content: t('common.reset') } }
-          ]
-        }
-      }
-    ]
-  },
-  columns: [
-    { type: 'seq', title: t('common.index'), width: 100 },
-    { field: 'name', title: '岗位名称' },
-    { field: 'code', title: '岗位编码' },
-    { field: 'sort', title: '岗位顺序' },
-    {
-      field: 'status',
-      title: t('common.status'),
-      slots: {
-        default: 'status_default'
-      }
-    },
-    {
-      field: 'createTime',
-      title: t('common.createTime'),
-      width: 160,
-      sortable: true,
-      formatter: 'formatDate'
-    },
-    {
-      field: 'action',
-      title: t('table.action'),
-      width: '240px',
-      showOverflow: true,
-      slots: {
-        default: 'action_default'
-      }
-    }
-  ],
-  pagerConfig: {
-    border: false,
-    background: false,
-    perfect: true,
-    pageSize: 10,
-    pagerCount: 7,
-    pageSizes: [5, 10, 15, 20, 50, 100, 200, 500],
-    layouts: ['PrevJump', 'PrevPage', 'Jump', 'PageCount', 'NextPage', 'NextJump', 'Sizes', 'Total']
-  },
-  proxyConfig: {
-    seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号)
-    form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为
-    props: { result: 'list', total: 'total' },
-    ajax: {
-      query: ({ page, form }) => {
-        const queryParams: PostPageReqVO = Object.assign({}, form)
-        queryParams.pageSize = page.pageSize
-        queryParams.pageNo = page.currentPage
-        return new Promise(async (resolve) => {
-          resolve(await PostApi.getPostPageApi(queryParams))
-        })
-      }
-    }
-  }
-})
+const gridOptions = useVxeGrid(allSchemas, PostApi.getPostPageApi)
 const formData = ref<PostVO>()
-const formItems = ref<VxeFormItemProps[]>([
-  { field: 'id', title: 'id', visible: false },
-  {
-    field: 'name',
-    title: '岗位名称',
-    span: 12,
-    itemRender: { name: '$input', props: { placeholder: '请输入岗位名称' } }
-  },
-  {
-    field: 'code',
-    title: '岗位编码',
-    span: 12,
-    itemRender: { name: '$input', props: { placeholder: '请输入岗位编码' } }
-  },
-  {
-    field: 'sort',
-    title: '岗位顺序',
-    span: 12,
-    itemRender: { name: '$input', props: { type: 'number', placeholder: '请输入岗位顺序' } }
-  },
-  {
-    field: 'status',
-    title: t('common.status'),
-    span: 12,
-    itemRender: {
-      name: '$select',
-      options: getIntDictOptions(DICT_TYPE.COMMON_STATUS),
-      props: { placeholder: '请选择' }
-    }
-  },
-  {
-    align: 'center',
-    span: 24,
-    itemRender: {
-      name: '$buttons',
-      children: [
-        { props: { type: 'submit', content: t('action.save'), status: 'primary' } },
-        { props: { type: 'reset', content: t('common.reset') } }
-      ]
-    }
-  }
-])
+const formItems = ref<VxeFormItemProps[]>(allSchemas.formSchema)
 // 设置标题
 const setDialogTile = (type: string) => {
   dialogTitle.value = t('action.' + type)
@@ -194,7 +121,7 @@ const handleDetail = (row: PostVO) => {
 // 新增操作
 const handleCreate = () => {
   setDialogTile('create')
-  xForm.value?.reset()
+  formData.value = undefined
 }
 
 // 修改操作
@@ -206,16 +133,13 @@ const handleUpdate = async (rowId: number) => {
 }
 // 删除操作
 const handleDelete = (rowId: number) => {
-  ElMessageBox.confirm(t('common.delMessage'), t('common.confirmTitle'), {
-    confirmButtonText: t('common.ok'),
-    cancelButtonText: t('common.cancel'),
-    type: 'warning'
-  })
+  message
+    .confirm(t('common.delMessage'), t('common.confirmTitle'))
     .then(async () => {
       await PostApi.deletePostApi(rowId)
+      message.success(t('common.delSuccess'))
     })
     .finally(() => {
-      ElMessage.success(t('common.delSuccess'))
       xGrid.value?.commitProxy('query')
     })
 }
@@ -227,10 +151,10 @@ const submitForm: VxeFormEvents.Submit = async () => {
     const data = formData.value as PostVO
     if (actionType.value === 'create') {
       await PostApi.createPostApi(data)
-      ElMessage.success(t('common.createSuccess'))
+      message.success(t('common.createSuccess'))
     } else {
       await PostApi.updatePostApi(data)
-      ElMessage.success(t('common.updateSuccess'))
+      message.success(t('common.updateSuccess'))
     }
     // 操作成功,重新加载列表
     dialogVisible.value = false
@@ -240,82 +164,3 @@ const submitForm: VxeFormEvents.Submit = async () => {
   }
 }
 </script>
-<template>
-  <ContentWrap>
-    <vxe-grid ref="xGrid" v-bind="gridOptions">
-      <template #toolbar_buttons>
-        <el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
-          <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
-        </el-button>
-      </template>
-      <template #status_default="{ row }">
-        <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
-      </template>
-      <template #action_default="{ row }">
-        <el-button
-          link
-          type="primary"
-          v-hasPermi="['system:post:update']"
-          @click="handleUpdate(row.id)"
-        >
-          <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
-        </el-button>
-        <el-button
-          link
-          type="primary"
-          v-hasPermi="['system:post:update']"
-          @click="handleDetail(row)"
-        >
-          <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
-        </el-button>
-        <el-button
-          link
-          type="primary"
-          v-hasPermi="['system:post:delete']"
-          @click="handleDelete(row.id)"
-        >
-          <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
-        </el-button>
-      </template>
-    </vxe-grid>
-  </ContentWrap>
-  <vxe-modal
-    v-model="dialogVisible"
-    id="myModal6"
-    :title="dialogTitle"
-    width="800"
-    height="400"
-    min-width="460"
-    min-height="320"
-    show-zoom
-    resize
-    remember
-    storage
-    transfer
-    show-footer
-  >
-    <template #default>
-      <!-- 对话框(添加 / 修改) -->
-      <vxe-form
-        ref="xForm"
-        v-if="['create', 'update'].includes(actionType)"
-        :data="formData"
-        :items="formItems"
-        :rules="rules"
-        @submit="submitForm"
-      />
-      <Descriptions
-        v-if="actionType === 'detail'"
-        :schema="allSchemas.detailSchema"
-        :data="detailRef"
-      >
-        <template #status="{ row }">
-          <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
-        </template>
-        <template #createTime="{ row }">
-          <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
-        </template>
-      </Descriptions>
-    </template>
-  </vxe-modal>
-</template>

+ 0 - 194
yudao-ui-admin-vue3/src/views/system/post/indexd.vue

@@ -1,194 +0,0 @@
-<script setup lang="ts">
-import { ref, unref } from 'vue'
-import dayjs from 'dayjs'
-import { ElMessage } from 'element-plus'
-import { DICT_TYPE } from '@/utils/dict'
-import { useTable } from '@/hooks/web/useTable'
-import { useI18n } from '@/hooks/web/useI18n'
-import { FormExpose } from '@/components/Form'
-import type { PostVO } from '@/api/system/post/types'
-import { rules, allSchemas } from './post.data'
-import * as PostApi from '@/api/system/post'
-const { t } = useI18n() // 国际化
-
-// ========== 列表相关 ==========
-const { register, tableObject, methods } = useTable<PostVO>({
-  getListApi: PostApi.getPostPageApi,
-  delListApi: PostApi.deletePostApi,
-  exportListApi: PostApi.exportPostApi
-})
-const { getList, setSearchParams, delList, exportList } = methods
-
-// ========== CRUD 相关 ==========
-const actionLoading = ref(false) // 遮罩层
-const actionType = ref('') // 操作按钮的类型
-const dialogVisible = ref(false) // 是否显示弹出层
-const dialogTitle = ref('edit') // 弹出层标题
-const formRef = ref<FormExpose>() // 表单 Ref
-
-// 设置标题
-const setDialogTile = (type: string) => {
-  dialogTitle.value = t('action.' + type)
-  actionType.value = type
-  dialogVisible.value = true
-}
-
-// 新增操作
-const handleCreate = () => {
-  setDialogTile('create')
-  // 重置表单
-  unref(formRef)?.getElFormRef()?.resetFields()
-}
-
-// 修改操作
-const handleUpdate = async (rowId: number) => {
-  setDialogTile('update')
-  // 设置数据
-  const res = await PostApi.getPostApi(rowId)
-  unref(formRef)?.setValues(res)
-}
-
-// 提交按钮
-const submitForm = async () => {
-  const elForm = unref(formRef)?.getElFormRef()
-  if (!elForm) return
-  elForm.validate(async (valid) => {
-    if (valid) {
-      actionLoading.value = true
-      // 提交请求
-      try {
-        const data = unref(formRef)?.formModel as PostVO
-        if (actionType.value === 'create') {
-          await PostApi.createPostApi(data)
-          ElMessage.success(t('common.createSuccess'))
-        } else {
-          await PostApi.updatePostApi(data)
-          ElMessage.success(t('common.updateSuccess'))
-        }
-        // 操作成功,重新加载列表
-        dialogVisible.value = false
-        await getList()
-      } finally {
-        actionLoading.value = false
-      }
-    }
-  })
-}
-
-// ========== 详情相关 ==========
-const detailRef = ref() // 详情 Ref
-
-// 详情操作
-const handleDetail = async (row: PostVO) => {
-  // 设置数据
-  detailRef.value = row
-  setDialogTile('detail')
-}
-
-// ========== 初始化 ==========
-getList()
-</script>
-
-<template>
-  <!-- 搜索工作区 -->
-  <ContentWrap>
-    <Search :schema="allSchemas.searchSchema" @search="setSearchParams" @reset="setSearchParams" />
-  </ContentWrap>
-  <ContentWrap>
-    <!-- 操作工具栏 -->
-    <div class="mb-10px">
-      <el-button type="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
-        <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
-      </el-button>
-      <el-button
-        type="warning"
-        v-hasPermi="['system:post:export']"
-        :loading="tableObject.exportLoading"
-        @click="exportList('岗位数据.xls')"
-      >
-        <Icon icon="ep:download" class="mr-5px" /> {{ t('action.export') }}
-      </el-button>
-    </div>
-    <!-- 列表 -->
-    <Table
-      :columns="allSchemas.tableColumns"
-      :selection="false"
-      :data="tableObject.tableList"
-      :loading="tableObject.loading"
-      :pagination="{
-        total: tableObject.total
-      }"
-      v-model:pageSize="tableObject.pageSize"
-      v-model:currentPage="tableObject.currentPage"
-      @register="register"
-    >
-      <template #status="{ row }">
-        <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
-      </template>
-      <template #createTime="{ row }">
-        <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
-      </template>
-      <template #action="{ row }">
-        <el-button
-          link
-          type="primary"
-          v-hasPermi="['system:post:update']"
-          @click="handleUpdate(row.id)"
-        >
-          <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
-        </el-button>
-        <el-button
-          link
-          type="primary"
-          v-hasPermi="['system:post:update']"
-          @click="handleDetail(row)"
-        >
-          <Icon icon="ep:view" class="mr-1px" /> {{ t('action.detail') }}
-        </el-button>
-        <el-button
-          link
-          type="primary"
-          v-hasPermi="['system:post:delete']"
-          @click="delList(row.id, false)"
-        >
-          <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
-        </el-button>
-      </template>
-    </Table>
-  </ContentWrap>
-
-  <Dialog v-model="dialogVisible" :title="dialogTitle">
-    <!-- 对话框(添加 / 修改) -->
-    <Form
-      v-if="['create', 'update'].includes(actionType)"
-      :schema="allSchemas.formSchema"
-      :rules="rules"
-      ref="formRef"
-    />
-    <!-- 对话框(详情) -->
-    <Descriptions
-      v-if="actionType === 'detail'"
-      :schema="allSchemas.detailSchema"
-      :data="detailRef"
-    >
-      <template #status="{ row }">
-        <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
-      </template>
-      <template #createTime="{ row }">
-        <span>{{ dayjs(row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>
-      </template>
-    </Descriptions>
-    <!-- 操作按钮 -->
-    <template #footer>
-      <el-button
-        v-if="['create', 'update'].includes(actionType)"
-        type="primary"
-        :loading="actionLoading"
-        @click="submitForm"
-      >
-        {{ t('action.save') }}
-      </el-button>
-      <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
-    </template>
-  </Dialog>
-</template>

+ 22 - 10
yudao-ui-admin-vue3/src/views/system/post/post.data.ts

@@ -1,7 +1,7 @@
 import { reactive } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'
 import { required } from '@/utils/formRules'
-import { CrudSchema, useCrudSchemas } from '@/hooks/web/useCrudSchemas'
+import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
 import { DICT_TYPE } from '@/utils/dict'
 const { t } = useI18n() // 国际化
 
@@ -13,9 +13,9 @@ export const rules = reactive({
 })
 
 // CrudSchema
-const crudSchemas = reactive<CrudSchema[]>([
+const crudSchemas = reactive<VxeCrudSchema[]>([
   {
-    label: t('common.index'),
+    title: t('common.index'),
     field: 'id',
     type: 'index',
     form: {
@@ -26,42 +26,54 @@ const crudSchemas = reactive<CrudSchema[]>([
     }
   },
   {
-    label: '岗位名称',
+    title: '岗位名称',
     field: 'name',
     search: {
       show: true
     }
   },
   {
-    label: '岗位编码',
+    title: '岗位编码',
     field: 'code',
     search: {
       show: true
     }
   },
   {
-    label: '岗位顺序',
+    title: '岗位顺序',
     field: 'sort'
   },
   {
-    label: t('common.status'),
+    title: t('common.status'),
     field: 'status',
     dictType: DICT_TYPE.COMMON_STATUS,
+    table: {
+      slots: {
+        default: 'status_default'
+      }
+    },
     search: {
       show: true
     }
   },
   {
-    label: t('common.createTime'),
+    title: t('common.createTime'),
     field: 'createTime',
     form: {
       show: false
     }
   },
   {
-    label: t('table.action'),
+    title: t('table.action'),
     field: 'action',
     width: '240px',
+    table: {
+      width: '240px',
+      showOverflow: true,
+      slots: {
+        default: 'action_default'
+      }
+    },
     form: {
       show: false
     },
@@ -70,4 +82,4 @@ const crudSchemas = reactive<CrudSchema[]>([
     }
   }
 ])
-export const { allSchemas } = useCrudSchemas(crudSchemas)
+export const { allSchemas } = useVxeCrudSchemas(crudSchemas)