@dongkboy 1 год назад
Родитель
Сommit
4312db541b
58 измененных файлов с 3644 добавлено и 836 удалено
  1. 4 2
      .env.development
  2. 1 1
      .env.production
  3. 114 33
      package-lock.json
  4. 4 0
      package.json
  5. 121 16
      pnpm-lock.yaml
  6. 10 4
      src/api/index.ts
  7. 8 11
      src/api/modules/app.ts
  8. 24 0
      src/api/modules/dept.ts
  9. 19 0
      src/api/modules/member.ts
  10. 4 5
      src/api/modules/role.ts
  11. 33 0
      src/api/modules/tenant.ts
  12. 13 9
      src/api/modules/user.ts
  13. 27 0
      src/api/types/deptTypes.ts
  14. 27 0
      src/api/types/memberTypes.ts
  15. 5 0
      src/assets/icons/arrow-right.svg
  16. 1 0
      src/assets/icons/caretRight.svg
  17. BIN
      src/assets/images/decoration1.png
  18. BIN
      src/assets/images/decoration2.png
  19. BIN
      src/assets/images/decoration3.png
  20. BIN
      src/assets/images/titleImg.png
  21. BIN
      src/assets/images/user/avatar1.png
  22. BIN
      src/assets/images/user/avatar2.png
  23. BIN
      src/assets/images/user/avatar3.png
  24. BIN
      src/assets/images/user/avatar4.png
  25. 20 0
      src/assets/styles/globals.css
  26. 5 4
      src/components/LoginForm/index.vue
  27. 4 1
      src/components/PageMain/index.vue
  28. 207 170
      src/components/Table/index.vue
  29. 208 0
      src/components/TableTree/index.vue
  30. 19 13
      src/layouts/components/Header/index.vue
  31. 90 68
      src/layouts/components/HotkeysIntro/index.vue
  32. 7 3
      src/layouts/components/Logo/index.vue
  33. 3 0
      src/layouts/components/Menu/sub.vue
  34. 126 20
      src/layouts/components/Topbar/Toolbar/rightSide.vue
  35. 10 4
      src/layouts/ui-kit/HDropdownMenu.vue
  36. 0 37
      src/router/modules/multilevel.menu.example.ts
  37. 60 9
      src/router/modules/organization.ts
  38. 25 4
      src/router/modules/personnel.ts
  39. 0 12
      src/router/routes.ts
  40. 1 1
      src/settings.ts
  41. 1 2
      src/store/modules/menu.ts
  42. 32 14
      src/store/modules/user.ts
  43. 3 0
      src/types/auto-imports.d.ts
  44. 1 0
      src/types/components.d.ts
  45. 19 0
      src/utils/composables/imageUtils.ts
  46. 30 0
      src/utils/composables/pinyinHelper.ts
  47. 0 15
      src/views/example/ceshi.vue
  48. 0 52
      src/views/example/index.vue
  49. 0 11
      src/views/organizationManagement/organization.vue
  50. 353 0
      src/views/organizationManagement/organization/organizationAdd.vue
  51. 452 0
      src/views/organizationManagement/organization/organizationDetail.vue
  52. 416 0
      src/views/organizationManagement/organization/organizationlist.vue
  53. 298 0
      src/views/organizationManagement/organization/subOrganizatioAdd.vue
  54. 221 0
      src/views/organizationManagement/tenantPermissions.vue
  55. 85 0
      src/views/personnel/memberAdd.vue
  56. 422 252
      src/views/personnel/memberManagement.vue
  57. 107 59
      src/views/personnel/roleManagement.vue
  58. 4 4
      themes/index.ts

+ 4 - 2
.env.development

@@ -4,7 +4,8 @@ VITE_APP_SETTING = true
 VITE_APP_TITLE = 掌柜运营平台
 # 接口请求地址,会设置到 axios 的 baseURL 参数上
 # VITE_APP_API_BASEURL = https://test.baoxianzhanggui.com/web-api #默认地址
-VITE_APP_API_BASEURL = http://192.168.5.148:8001  #田智
+# VITE_APP_API_BASEURL = http://192.168.0.55:8001  #董鑫
+VITE_APP_API_BASEURL = http://192.168.0.253:8001  #田智
 
 # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
 VITE_APP_DEBUG_TOOL =
@@ -14,4 +15,5 @@ VITE_APP_DISABLE_DEVTOOL = false
 # 是否开启代理
 VITE_OPEN_PROXY = false
 # 是否开启开发者工具
-VITE_OPEN_DEVTOOLS = false
+VITE_OPEN_DEVTOOLS = true
+

+ 1 - 1
.env.production

@@ -7,7 +7,7 @@ VITE_APP_API_BASEURL = https://sxzgkj.baoxianzhanggui.com/web-api
 # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
 VITE_APP_DEBUG_TOOL =
 # 是否禁用开发者工具,可防止被调试
-VITE_APP_DISABLE_DEVTOOL = false
+VITE_APP_DISABLE_DEVTOOL = true
 
 # 是否在打包时启用 Mock
 VITE_BUILD_MOCK = false

+ 114 - 33
package-lock.json

@@ -17,6 +17,7 @@
         "dayjs": "^1.11.13",
         "defu": "^6.1.4",
         "disable-devtool": "^0.3.8",
+        "element-china-area-data": "^6.1.0",
         "element-plus": "^2.9.0",
         "eruda": "^3.4.1",
         "floating-vue": "5.2.2",
@@ -30,6 +31,7 @@
         "path-browserify": "^1.0.1",
         "path-to-regexp": "^8.2.0",
         "pinia": "^2.3.0",
+        "pinyin": "4.0.0-alpha.2",
         "qs": "^6.13.1",
         "scule": "^1.3.0",
         "vconsole": "^3.15.1",
@@ -46,6 +48,7 @@
         "@types/mockjs": "^1.0.10",
         "@types/nprogress": "^0.2.3",
         "@types/path-browserify": "^1.0.3",
+        "@types/pinyin": "^2.10.2",
         "@types/qs": "^6.9.17",
         "@unocss/eslint-plugin": "^0.65.1",
         "@vitejs/plugin-legacy": "^6.0.0",
@@ -4090,6 +4093,13 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/pinyin": {
+      "version": "2.10.2",
+      "resolved": "https://registry.npmmirror.com/@types/pinyin/-/pinyin-2.10.2.tgz",
+      "integrity": "sha512-jLzlRkaLRLg+lgYPjOuP3HX2cozUkhXls5GTXopsKuKJ9lDGlIAb88OoIztH6TbNUsoJnl/7e/kjaumA5IKKJg==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/qs": {
       "version": "6.9.17",
       "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.17.tgz",
@@ -6407,7 +6417,7 @@
       "version": "4.1.2",
       "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz",
       "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "pascal-case": "^3.1.2",
@@ -6452,7 +6462,7 @@
       "version": "1.0.4",
       "resolved": "https://registry.npmmirror.com/capital-case/-/capital-case-1.0.4.tgz",
       "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "no-case": "^3.0.4",
@@ -6485,25 +6495,12 @@
       }
     },
     "node_modules/change-case": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmmirror.com/change-case/-/change-case-4.1.2.tgz",
-      "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==",
-      "devOptional": true,
+      "version": "5.4.4",
+      "resolved": "https://registry.npmmirror.com/change-case/-/change-case-5.4.4.tgz",
+      "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==",
       "license": "MIT",
-      "dependencies": {
-        "camel-case": "^4.1.2",
-        "capital-case": "^1.0.4",
-        "constant-case": "^3.0.4",
-        "dot-case": "^3.0.4",
-        "header-case": "^2.0.4",
-        "no-case": "^3.0.4",
-        "param-case": "^3.0.4",
-        "pascal-case": "^3.1.2",
-        "path-case": "^3.0.4",
-        "sentence-case": "^3.0.4",
-        "snake-case": "^3.0.4",
-        "tslib": "^2.0.3"
-      }
+      "optional": true,
+      "peer": true
     },
     "node_modules/character-entities": {
       "version": "2.0.2",
@@ -6523,6 +6520,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/china-division": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmmirror.com/china-division/-/china-division-2.7.0.tgz",
+      "integrity": "sha512-4uUPAT+1WfqDh5jytq7omdCmHNk3j+k76zEG/2IqaGcYB90c2SwcixttcypdsZ3T/9tN1TTpBDoeZn+Yw/qBEA==",
+      "license": "MIT"
+    },
     "node_modules/chokidar": {
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
@@ -6984,7 +6987,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/constant-case/-/constant-case-3.0.4.tgz",
       "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "no-case": "^3.0.4",
@@ -7708,7 +7711,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz",
       "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "no-case": "^3.0.4",
@@ -7763,6 +7766,15 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/element-china-area-data": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/element-china-area-data/-/element-china-area-data-6.1.0.tgz",
+      "integrity": "sha512-IkpcjwQv2A/2AxFiSoaISZ+oMw1rZCPUSOg5sOCwT5jKc96TaawmKZeY81xfxXsO0QbKxU5LLc6AirhG52hUmg==",
+      "license": "MIT",
+      "dependencies": {
+        "china-division": "^2.7.0"
+      }
+    },
     "node_modules/element-plus": {
       "version": "2.9.1",
       "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.1.tgz",
@@ -10226,7 +10238,7 @@
       "version": "2.0.4",
       "resolved": "https://registry.npmmirror.com/header-case/-/header-case-2.0.4.tgz",
       "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "capital-case": "^1.0.4",
@@ -11991,6 +12003,12 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/keypress": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/keypress/-/keypress-0.1.0.tgz",
+      "integrity": "sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==",
+      "license": "MIT"
+    },
     "node_modules/keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
@@ -12464,7 +12482,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz",
       "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "tslib": "^2.0.3"
@@ -13840,7 +13858,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz",
       "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "lower-case": "^2.0.2",
@@ -13912,6 +13930,27 @@
         "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
+    "node_modules/node-plop/node_modules/change-case": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/change-case/-/change-case-4.1.2.tgz",
+      "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "camel-case": "^4.1.2",
+        "capital-case": "^1.0.4",
+        "constant-case": "^3.0.4",
+        "dot-case": "^3.0.4",
+        "header-case": "^2.0.4",
+        "no-case": "^3.0.4",
+        "param-case": "^3.0.4",
+        "pascal-case": "^3.1.2",
+        "path-case": "^3.0.4",
+        "sentence-case": "^3.0.4",
+        "snake-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
     "node_modules/node-plop/node_modules/cli-cursor": {
       "version": "3.1.0",
       "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -14770,7 +14809,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz",
       "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "dot-case": "^3.0.4",
@@ -14892,7 +14931,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz",
       "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "no-case": "^3.0.4",
@@ -14919,7 +14958,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/path-case/-/path-case-3.0.4.tgz",
       "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "dot-case": "^3.0.4",
@@ -15097,6 +15136,48 @@
         }
       }
     },
+    "node_modules/pinyin": {
+      "version": "4.0.0-alpha.2",
+      "resolved": "https://registry.npmmirror.com/pinyin/-/pinyin-4.0.0-alpha.2.tgz",
+      "integrity": "sha512-SED2wWr1X0QwH6rXIDgg20zS1mAk0AVMO8eM3KomUlOYzC8mNMWZnspZWhhI0M8MBIbF2xwa+5r30jTSjAqNsg==",
+      "license": "MIT",
+      "dependencies": {
+        "commander": "~1.1.1"
+      },
+      "bin": {
+        "pinyin": "bin/pinyin"
+      },
+      "engines": {
+        "install-node": "^18.0.0"
+      },
+      "peerDependencies": {
+        "@node-rs/jieba": "^1.6.0",
+        "nodejieba": "2.5.2",
+        "segmentit": "^2.0.3"
+      },
+      "peerDependenciesMeta": {
+        "@node-rs/jieba": {
+          "optional": true
+        },
+        "nodejieba": {
+          "optional": true
+        },
+        "segmentit": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinyin/node_modules/commander": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-1.1.1.tgz",
+      "integrity": "sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==",
+      "dependencies": {
+        "keypress": "0.1.x"
+      },
+      "engines": {
+        "node": ">= 0.6.x"
+      }
+    },
     "node_modules/pkg-types": {
       "version": "1.3.0",
       "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.0.tgz",
@@ -16954,7 +17035,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/sentence-case/-/sentence-case-3.0.4.tgz",
       "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "no-case": "^3.0.4",
@@ -17209,7 +17290,7 @@
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/snake-case/-/snake-case-3.0.4.tgz",
       "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "dot-case": "^3.0.4",
@@ -19277,7 +19358,7 @@
       "version": "2.8.1",
       "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
       "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
-      "devOptional": true,
+      "dev": true,
       "license": "0BSD"
     },
     "node_modules/tsx": {
@@ -20183,7 +20264,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/upper-case/-/upper-case-2.0.2.tgz",
       "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "tslib": "^2.0.3"
@@ -20193,7 +20274,7 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmmirror.com/upper-case-first/-/upper-case-first-2.0.2.tgz",
       "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==",
-      "devOptional": true,
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "tslib": "^2.0.3"

+ 4 - 0
package.json

@@ -31,6 +31,7 @@
     "dayjs": "^1.11.13",
     "defu": "^6.1.4",
     "disable-devtool": "^0.3.8",
+    "element-china-area-data": "^6.1.0",
     "element-plus": "^2.9.0",
     "eruda": "^3.4.1",
     "floating-vue": "5.2.2",
@@ -44,8 +45,10 @@
     "path-browserify": "^1.0.1",
     "path-to-regexp": "^8.2.0",
     "pinia": "^2.3.0",
+    "pinyin": "4.0.0-alpha.2",
     "qs": "^6.13.1",
     "scule": "^1.3.0",
+    "segmentit": "^2.0.3",
     "vconsole": "^3.15.1",
     "vue": "^3.5.13",
     "vue-m-message": "^4.0.2",
@@ -60,6 +63,7 @@
     "@types/mockjs": "^1.0.10",
     "@types/nprogress": "^0.2.3",
     "@types/path-browserify": "^1.0.3",
+    "@types/pinyin": "^2.10.2",
     "@types/qs": "^6.9.17",
     "@unocss/eslint-plugin": "^0.65.1",
     "@vitejs/plugin-legacy": "^6.0.0",

+ 121 - 16
pnpm-lock.yaml

@@ -35,6 +35,9 @@ importers:
       disable-devtool:
         specifier: ^0.3.8
         version: 0.3.8
+      element-china-area-data:
+        specifier: ^6.1.0
+        version: 6.1.0
       element-plus:
         specifier: ^2.9.0
         version: 2.9.0(vue@3.5.13(typescript@5.6.3))
@@ -74,12 +77,18 @@ importers:
       pinia:
         specifier: ^2.3.0
         version: 2.3.0(typescript@5.6.3)(vue@3.5.13(typescript@5.6.3))
+      pinyin:
+        specifier: 4.0.0-alpha.2
+        version: 4.0.0-alpha.2(segmentit@2.0.3)
       qs:
         specifier: ^6.13.1
         version: 6.13.1
       scule:
         specifier: ^1.3.0
         version: 1.3.0
+      segmentit:
+        specifier: ^2.0.3
+        version: 2.0.3
       vconsole:
         specifier: ^3.15.1
         version: 3.15.1
@@ -117,6 +126,9 @@ importers:
       '@types/path-browserify':
         specifier: ^1.0.3
         version: 1.0.3
+      '@types/pinyin':
+        specifier: ^2.10.2
+        version: 2.10.2
       '@types/qs':
         specifier: ^6.9.17
         version: 6.9.17
@@ -1639,9 +1651,15 @@ packages:
   '@types/nprogress@0.2.3':
     resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==}
 
+  '@types/parse-json@4.0.2':
+    resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
+
   '@types/path-browserify@1.0.3':
     resolution: {integrity: sha512-ZmHivEbNCBtAfcrFeBCiTjdIc2dey0l7oCGNGpSuRTy8jP6UVND7oUowlvDujBy8r2Hoa8bfFUOCiPWfmtkfxw==}
 
+  '@types/pinyin@2.10.2':
+    resolution: {integrity: sha512-jLzlRkaLRLg+lgYPjOuP3HX2cozUkhXls5GTXopsKuKJ9lDGlIAb88OoIztH6TbNUsoJnl/7e/kjaumA5IKKJg==}
+
   '@types/qs@6.9.17':
     resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==}
 
@@ -2145,6 +2163,9 @@ packages:
   b4a@1.6.6:
     resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
 
+  babel-plugin-macros@2.8.0:
+    resolution: {integrity: sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==}
+
   babel-plugin-polyfill-corejs2@0.4.12:
     resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==}
     peerDependencies:
@@ -2160,6 +2181,10 @@ packages:
     peerDependencies:
       '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
 
+  babel-plugin-preval@4.0.0:
+    resolution: {integrity: sha512-fZI/4cYneinlj2k/FsXw0/lTWSC5KKoepUueS1g25Gb5vx3GrRyaVwxWCshYqx11GEU4mZnbbFhee8vpquFS2w==}
+    engines: {node: '>=8', npm: '>=6'}
+
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
@@ -2340,6 +2365,9 @@ packages:
   chardet@0.7.0:
     resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
 
+  china-division@2.7.0:
+    resolution: {integrity: sha512-4uUPAT+1WfqDh5jytq7omdCmHNk3j+k76zEG/2IqaGcYB90c2SwcixttcypdsZ3T/9tN1TTpBDoeZn+Yw/qBEA==}
+
   chokidar@3.6.0:
     resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
     engines: {node: '>= 8.10.0'}
@@ -2435,6 +2463,10 @@ packages:
     resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
     engines: {node: '>= 0.8'}
 
+  commander@1.1.1:
+    resolution: {integrity: sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==}
+    engines: {node: '>= 0.6.x'}
+
   commander@12.1.0:
     resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
     engines: {node: '>=18'}
@@ -2512,6 +2544,10 @@ packages:
     resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==}
     engines: {node: '>= 0.4.0'}
 
+  cosmiconfig@6.0.0:
+    resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==}
+    engines: {node: '>=8'}
+
   cosmiconfig@9.0.0:
     resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==}
     engines: {node: '>=14'}
@@ -2767,6 +2803,9 @@ packages:
   electron-to-chromium@1.5.68:
     resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==}
 
+  element-china-area-data@6.1.0:
+    resolution: {integrity: sha512-IkpcjwQv2A/2AxFiSoaISZ+oMw1rZCPUSOg5sOCwT5jKc96TaawmKZeY81xfxXsO0QbKxU5LLc6AirhG52hUmg==}
+
   element-plus@2.9.0:
     resolution: {integrity: sha512-ccOFXKsauo2dtokAr4OX7gZsb7TuAoVxA2zGRZo5o2yyDDBLBaZxOoFQPoxITSLcHbBfQuNDGK5Iag5hnyKkZA==}
     peerDependencies:
@@ -3444,10 +3483,6 @@ packages:
     resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==}
     engines: {node: '>=0.10.0'}
 
-  hasown@2.0.0:
-    resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
-    engines: {node: '>= 0.4'}
-
   hasown@2.0.2:
     resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
     engines: {node: '>= 0.4'}
@@ -3610,9 +3645,6 @@ packages:
     resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
     engines: {node: '>=6'}
 
-  is-core-module@2.13.1:
-    resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
-
   is-core-module@2.15.1:
     resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==}
     engines: {node: '>= 0.4'}
@@ -3861,6 +3893,9 @@ packages:
   jsonfile@6.1.0:
     resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
 
+  keypress@0.1.0:
+    resolution: {integrity: sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==}
+
   keyv@4.5.4:
     resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
 
@@ -4617,6 +4652,22 @@ packages:
       typescript:
         optional: true
 
+  pinyin@4.0.0-alpha.2:
+    resolution: {integrity: sha512-SED2wWr1X0QwH6rXIDgg20zS1mAk0AVMO8eM3KomUlOYzC8mNMWZnspZWhhI0M8MBIbF2xwa+5r30jTSjAqNsg==}
+    engines: {install-node: ^18.0.0}
+    hasBin: true
+    peerDependencies:
+      '@node-rs/jieba': ^1.6.0
+      nodejieba: 2.5.2
+      segmentit: ^2.0.3
+    peerDependenciesMeta:
+      '@node-rs/jieba':
+        optional: true
+      nodejieba:
+        optional: true
+      segmentit:
+        optional: true
+
   pkg-types@1.2.1:
     resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
 
@@ -4729,6 +4780,9 @@ packages:
     resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==}
     engines: {node: '>=18'}
 
+  preval.macro@4.0.0:
+    resolution: {integrity: sha512-sJJnE71X+MPr64CVD2AurmUj4JEDqbudYbStav3L9Xjcqm4AR0ymMm6sugw1mUmfI/7gw4JWA4JXo/k6w34crw==}
+
   process-nextick-args@2.0.1:
     resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
 
@@ -5092,6 +5146,9 @@ packages:
   secure-compare@3.0.1:
     resolution: {integrity: sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=}
 
+  segmentit@2.0.3:
+    resolution: {integrity: sha512-7mn2XL3OdTUQ+AhHz7SbgyxLTaQRzTWQNVwiK+UlTO8aePGbSwvKUzTwE4238+OUY9MoR6ksAg35zl8sfTunQQ==}
+
   semver@5.7.2:
     resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
     hasBin: true
@@ -6032,6 +6089,10 @@ packages:
     resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==}
     engines: {node: ^14.17.0 || >=16.0.0}
 
+  yaml@1.10.2:
+    resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+    engines: {node: '>= 6'}
+
   yaml@2.5.1:
     resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
     engines: {node: '>= 14'}
@@ -7461,8 +7522,12 @@ snapshots:
 
   '@types/nprogress@0.2.3': {}
 
+  '@types/parse-json@4.0.2': {}
+
   '@types/path-browserify@1.0.3': {}
 
+  '@types/pinyin@2.10.2': {}
+
   '@types/qs@6.9.17': {}
 
   '@types/svgo@2.6.4':
@@ -8116,6 +8181,12 @@ snapshots:
 
   b4a@1.6.6: {}
 
+  babel-plugin-macros@2.8.0:
+    dependencies:
+      '@babel/runtime': 7.26.0
+      cosmiconfig: 6.0.0
+      resolve: 1.22.8
+
   babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0):
     dependencies:
       '@babel/compat-data': 7.26.3
@@ -8140,6 +8211,12 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  babel-plugin-preval@4.0.0:
+    dependencies:
+      '@babel/runtime': 7.26.0
+      babel-plugin-macros: 2.8.0
+      require-from-string: 2.0.2
+
   balanced-match@1.0.2: {}
 
   balanced-match@2.0.0: {}
@@ -8379,6 +8456,8 @@ snapshots:
 
   chardet@0.7.0: {}
 
+  china-division@2.7.0: {}
+
   chokidar@3.6.0:
     dependencies:
       anymatch: 3.1.3
@@ -8472,6 +8551,10 @@ snapshots:
     dependencies:
       delayed-stream: 1.0.0
 
+  commander@1.1.1:
+    dependencies:
+      keypress: 0.1.0
+
   commander@12.1.0: {}
 
   commander@2.20.3: {}
@@ -8533,6 +8616,14 @@ snapshots:
 
   corser@2.0.1: {}
 
+  cosmiconfig@6.0.0:
+    dependencies:
+      '@types/parse-json': 4.0.2
+      import-fresh: 3.3.0
+      parse-json: 5.2.0
+      path-type: 4.0.0
+      yaml: 1.10.2
+
   cosmiconfig@9.0.0(typescript@5.6.3):
     dependencies:
       env-paths: 2.2.1
@@ -8777,6 +8868,10 @@ snapshots:
 
   electron-to-chromium@1.5.68: {}
 
+  element-china-area-data@6.1.0:
+    dependencies:
+      china-division: 2.7.0
+
   element-plus@2.9.0(vue@3.5.13(typescript@5.6.3)):
     dependencies:
       '@ctrl/tinycolor': 3.6.1
@@ -9632,10 +9727,6 @@ snapshots:
       is-number: 3.0.0
       kind-of: 4.0.0
 
-  hasown@2.0.0:
-    dependencies:
-      function-bind: 1.1.2
-
   hasown@2.0.2:
     dependencies:
       function-bind: 1.1.2
@@ -9825,10 +9916,6 @@ snapshots:
     dependencies:
       builtin-modules: 3.3.0
 
-  is-core-module@2.13.1:
-    dependencies:
-      hasown: 2.0.0
-
   is-core-module@2.15.1:
     dependencies:
       hasown: 2.0.2
@@ -10008,6 +10095,8 @@ snapshots:
     optionalDependencies:
       graceful-fs: 4.2.11
 
+  keypress@0.1.0: {}
+
   keyv@4.5.4:
     dependencies:
       json-buffer: 3.0.1
@@ -10940,6 +11029,12 @@ snapshots:
     transitivePeerDependencies:
       - '@vue/composition-api'
 
+  pinyin@4.0.0-alpha.2(segmentit@2.0.3):
+    dependencies:
+      commander: 1.1.1
+    optionalDependencies:
+      segmentit: 2.0.3
+
   pkg-types@1.2.1:
     dependencies:
       confbox: 0.1.8
@@ -11064,6 +11159,10 @@ snapshots:
     dependencies:
       parse-ms: 4.0.0
 
+  preval.macro@4.0.0:
+    dependencies:
+      babel-plugin-preval: 4.0.0
+
   process-nextick-args@2.0.1: {}
 
   process@0.11.10: {}
@@ -11227,7 +11326,7 @@ snapshots:
 
   resolve@1.22.8:
     dependencies:
-      is-core-module: 2.13.1
+      is-core-module: 2.15.1
       path-parse: 1.0.7
       supports-preserve-symlinks-flag: 1.0.0
 
@@ -11412,6 +11511,10 @@ snapshots:
 
   secure-compare@3.0.1: {}
 
+  segmentit@2.0.3:
+    dependencies:
+      preval.macro: 4.0.0
+
   semver@5.7.2: {}
 
   semver@6.3.1: {}
@@ -12464,6 +12567,8 @@ snapshots:
       lodash: 4.17.21
       yaml: 2.6.1
 
+  yaml@1.10.2: {}
+
   yaml@2.5.1: {}
 
   yaml@2.6.1: {}

+ 10 - 4
src/api/index.ts

@@ -5,9 +5,12 @@ import axios from 'axios'
 // import qs from 'qs'
 import Message from 'vue-m-message'
 // 导入 API 模块
-import appApi from './modules/app'
-import userApi from './modules/user'
+import toolApi from './modules/app'
+import user from './modules/user'
 import roleApi from './modules/role'
+import tenantApi from './modules/tenant'
+import memberApi from './modules/member'
+import dept from './modules/dept'
 
 const axiosInstance:ApiInstance = axios.create({
   baseURL: (import.meta.env.DEV && import.meta.env.VITE_OPEN_PROXY === 'true') ? '/proxy/' : import.meta.env.VITE_APP_API_BASEURL,
@@ -89,8 +92,11 @@ axiosInstance.interceptors.response.use(
 )
   // API 模块
 const api = {
-  appApi: appApi(axiosInstance),
-  userApi: userApi(axiosInstance),
+  toolApi: toolApi(axiosInstance),
+  user: user(axiosInstance),
   roleApi: roleApi(axiosInstance),
+  tenantApi: tenantApi(axiosInstance),
+  member:memberApi(axiosInstance),
+  dept:dept(axiosInstance)
 }
 export default api

+ 8 - 11
src/api/modules/app.ts

@@ -1,15 +1,12 @@
 import { ApiInstance } from '../type'
 
-const appApi = (axiosInstance: ApiInstance) => ({
-  // 后端获取路由数据
-  routeList: () => axiosInstance.get('app/route/list', {
-    baseURL: '/mock/',
-  }),
-
-  // 基于文件系统路由模式下,后端获取导航菜单数据
-  menuList: () => axiosInstance.get('app/menu/list', {
-    baseURL: '/mock/',
-  }),
+const toolApi = (axiosInstance: ApiInstance) => ({
+
+  //上传接口
+  fileupload: (data: any) => axiosInstance.post('file/upload', data, {}),
+  //字典接口
+  dictType: (params: any) => axiosInstance.get('dict/get', {params}),
+
 })
 
-export default appApi
+export default toolApi

+ 24 - 0
src/api/modules/dept.ts

@@ -0,0 +1,24 @@
+import { ApiInstance } from '../type'
+
+
+const dept = (axiosInstance: ApiInstance) => ({
+  //机构新增接口
+  deptAdd: (data: any) => axiosInstance.post('dept/add', data),
+  //机构删除
+  deptDelete: (data: any) => axiosInstance.post('dept/delete', data),
+  //机构修改
+  deptUpdate: (data: any) => axiosInstance.post('dept/update', data),
+  //批量设置机构负责人
+  deptBatchLeader: (data: any) => axiosInstance.post('dept/batchLeader', data),
+  //机构批量删除
+  deptBatchDeleteDept: (data: any) => axiosInstance.post('dept/batchDeleteDept', data),
+  //机构树形查询
+  getDeptTree: (data?: any) => axiosInstance.post('dept/getDeptTree', data),
+  //机构区划查询
+  queryTree: () => axiosInstance.post('area/queryTree'),
+  // 机构信息查询
+  deptGet: (data: any) => axiosInstance.get(`dept/deptGet?id=${data}`),
+
+})
+
+export default dept

+ 19 - 0
src/api/modules/member.ts

@@ -0,0 +1,19 @@
+import { ApiInstance } from '../type'
+import * as Types from '../types/memberTypes';
+const memberApi = (axiosInstance: ApiInstance) => ({
+
+  //成员列表
+  userList: (data: any) => axiosInstance.post('user/userList', data),
+  //成员新增
+  userAdd: (data: any) => axiosInstance.post('user/userAdd', data),
+  //成员删除
+  userDelete: (data: any) => axiosInstance.delete(`user/userDelete?ids=${data}`),
+  //成员启用禁用
+  upAndDown: (data: Types.upAndDownRequest) => axiosInstance.post('user/upAndDown', data),
+  //成员查询
+  userGet: (params: Types.userGetRequest) => axiosInstance.get('user/userGet', {params}),
+  //成员修改
+  userEdit: (data: any) => axiosInstance.put('user/userEdit', data),
+})
+
+export default memberApi

+ 4 - 5
src/api/modules/role.ts

@@ -6,11 +6,6 @@ interface roleData {
   permsId: (number| string)[],
   perms: object[]
 }
-interface roleList {
-  pageNum: number;
-  pageSize: number;
-  name?:string,
-}
 interface roleDelete{
   id:string,
 }
@@ -18,6 +13,10 @@ interface roleDeleteValid{
   systemCode:string,
   id:string,
 }
+interface roleList{
+  name:string,
+
+}
 const roleApi = (axiosInstance: ApiInstance) => ({
   // 新增角色
   roleAdd: (data: roleData) => axiosInstance.post('userRole/roleAdd', data, {}),

+ 33 - 0
src/api/modules/tenant.ts

@@ -0,0 +1,33 @@
+import { ApiInstance } from '../type'
+
+interface sysList {
+  pageNum: number;
+  pageSize: number;
+  [key: string]: any
+}
+interface sysUpAndDown {
+  ids: any
+  opType: number,
+}
+const tenantApi = (axiosInstance: ApiInstance) => ({
+  // 租户列表
+  sysList: (data: sysList) => axiosInstance.post('system/sysList', data,),
+  // 添加
+  sysAdd: (data: any) => axiosInstance.post('system/sysAdd', data,),
+  // 租户修改
+  sysEdit: (data: any) => axiosInstance.put('system/sysEdit', data,),
+  // //详情
+  sysGet: (params: Record<string, any>) => axiosInstance.get('system/sysGet', { params }),
+  // 启用/禁用租户
+  sysUpAndDown: (data: sysUpAndDown) => axiosInstance.post('system/sysUpAndDown', data),
+  //批量删除
+  sysDelete: (data: Record<string, any>) => axiosInstance.delete(`system/sysDelete?ids=${data}`),
+  //获取租户权限
+  getSystemPerms: (data: any) => axiosInstance.post('system/getSystemPerms', data),
+  //修改租户权限
+  updatePerms: (data: any) => axiosInstance.post('system/updatePerms', data),
+  //获取租户平台菜单数据
+  getSystemConfig: () => axiosInstance.get('system/getSystemConfig'),
+})
+
+export default tenantApi

+ 13 - 9
src/api/modules/user.ts

@@ -7,11 +7,13 @@ interface LoginData {
   captchaId: number
 }
 interface PasswordEditData {
-  password: string
-  newpassword: string
+  oldPassWord: string
+  newPassWord: string
 }
-
-const userApi = (axiosInstance: ApiInstance) => ({
+interface changeHeadSculpture{
+  id:string,
+}
+const user = (axiosInstance: ApiInstance) => ({
   // 登录
   login: (data: LoginData) => axiosInstance.post('auth/login', data, {
 
@@ -19,11 +21,13 @@ const userApi = (axiosInstance: ApiInstance) => ({
   // 获取权限
   permission: () => axiosInstance.get('auth/getUserPer', {
   }),
-
   // 修改密码
-  passwordEdit: (data: PasswordEditData) => axiosInstance.post('user/password/edit', data, {
-    baseURL: '/mock/',
-  }),
+  passwordEdit: (data: PasswordEditData) => axiosInstance.post('auth/modifyPass', data),
+  //获取个人信息
+  getUserInfo: () => axiosInstance.get('auth/getUserInfo'),
+  //更换头像
+  changeHeadSculpture: (data:changeHeadSculpture) => axiosInstance.post('auth/changeHeadSculpture',data),
+
 })
 
-export default userApi
+export default user

+ 27 - 0
src/api/types/deptTypes.ts

@@ -0,0 +1,27 @@
+/**
+ * UpAndDownVo,启用/禁用实体
+ */
+export interface upAndDownRequest {
+  /**
+   * id集合
+   */
+  ids?: string[];
+  /**
+   * 操作类型
+   */
+  opType?: OpType;
+  [property: string]: any;
+}
+
+/**
+* 操作类型
+*/
+export enum OpType {
+  The0 = 0,
+  The1 = 1,
+}
+
+export type userGetRequest = {
+  id: string;
+  [property: string]: any;
+}

+ 27 - 0
src/api/types/memberTypes.ts

@@ -0,0 +1,27 @@
+/**
+ * UpAndDownVo,启用/禁用实体
+ */
+export interface upAndDownRequest {
+  /**
+   * id集合
+   */
+  ids?: string[];
+  /**
+   * 操作类型
+   */
+  opType?: OpType;
+  [property: string]: any;
+}
+
+/**
+* 操作类型
+*/
+export enum OpType {
+  The0 = 0,
+  The1 = 1,
+}
+
+export type userGetRequest = {
+  id: string;
+  [property: string]: any;
+}

+ 5 - 0
src/assets/icons/arrow-right.svg

@@ -0,0 +1,5 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="ICON/func/selector/icon_func_dropdrownCollapse">
+<path id="icon" fill-rule="evenodd" clip-rule="evenodd" d="M12 9.48685C12 9.37418 11.9583 9.26034 11.8749 9.16527L7.43216 4.19131C7.324 4.07042 7.16109 4 6.98776 4C6.81574 4 6.65153 4.07042 6.54466 4.19131L2.1254 9.14415C1.92861 9.36479 1.9677 9.68755 2.21271 9.86477C2.45772 10.042 2.81611 10.0068 3.0129 9.78614L6.98907 5.33093L10.9874 9.80844C11.1842 10.0291 11.5426 10.0643 11.7876 9.88707C11.927 9.78496 12 9.63708 12 9.48685Z" fill="#333333"/>
+</g>
+</svg>

+ 1 - 0
src/assets/icons/caretRight.svg

@@ -0,0 +1 @@
+<svg  xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M384 192v640l384-320.064z"></path></svg>

BIN
src/assets/images/decoration1.png


BIN
src/assets/images/decoration2.png


BIN
src/assets/images/decoration3.png


BIN
src/assets/images/titleImg.png


BIN
src/assets/images/user/avatar1.png


BIN
src/assets/images/user/avatar2.png


BIN
src/assets/images/user/avatar3.png


BIN
src/assets/images/user/avatar4.png


+ 20 - 0
src/assets/styles/globals.css

@@ -152,6 +152,22 @@ textarea {
   border-radius: 8px;
 }
 
+.bg-\[var\(--g-sub-sidebar-menu-active-bg\)\]\! {
+  background: var(--g-sub-sidebar-menu-active-bg) /* var(--g-sub-sidebar-menu-active-bg) */ !important;
+}
+
+.bg-\[var\(--g-header-menu-active-bg\)\]\! {
+  background-color: #ffffff1a /* var(--g-header-menu-active-bg) */ !important;
+}
+
+.text-\[var\(--g-header-menu-active-color\)\]\! {
+  color: #fff /* var(--g-header-menu-active-color) */ !important;
+}
+
+.sub-menu {
+  background: #f6fbff;
+}
+
 .height100 {
   height: 100%;
 }
@@ -216,6 +232,10 @@ textarea {
   margin-top: 10px;
 }
 
+.m-top-20 {
+  margin-top: 20px;
+}
+
 .m-btn-10 {
   margin-bottom: 10px;
 }

+ 5 - 4
src/components/LoginForm/index.vue

@@ -135,6 +135,7 @@ function handleLogin() {
 }
 
 .loginCSS {
+  height: 50px;
   padding: 2px;
   font-size: 20px;
   background: linear-gradient(91deg, #4fcaff 0%, #2b62ff 100%);
@@ -159,7 +160,7 @@ function handleLogin() {
       </div>
       <div>
         <ElFormItem prop="userName">
-          <ElInput v-model="form.userName" placeholder="手机号或工号" type="text" tabindex="1">
+          <ElInput v-model="form.userName" placeholder="输入手机号或用户名" type="text" tabindex="1">
             <template #prefix>
               <el-icon><User /></el-icon>
             </template>
@@ -175,14 +176,14 @@ function handleLogin() {
         </ElFormItem>
 
         <ElFormItem prop="captcha" style="margin-bottom: 0;">
-          <ElCol :md="14" :sm="14" :xs="14">
+          <ElCol :md="16" :sm="14" :xs="14">
             <ElInput v-model="form.captcha" placeholder="验证码" type="text" tabindex="3">
               <template #prefix>
                 <el-icon><CircleCheck /></el-icon>
               </template>
             </ElInput>
           </ElCol>
-          <ElCol :md="10" :sm="10" :xs="10" flex>
+          <ElCol :md="8" :sm="8" :xs="8" flex>
             <img style="width: 100%; height: 32px; margin-left: 20px;" class="pointer" :src="captchaSrc"
               @click="refreshCaptcha" />
           </ElCol>
@@ -196,7 +197,7 @@ function handleLogin() {
           忘记密码
         </ElLink>
       </div>
-      <ElButton class="loginCSS" :loading="loading" type="primary" 
+      <ElButton class="loginCSS" :loading="loading" type="primary"
       @keydown.enter="handleEnter"
       style="width: 100%;" @click.prevent="handleLogin">
         登录

+ 4 - 1
src/components/PageMain/index.vue

@@ -25,7 +25,7 @@ function unCollaspe() {
 </script>
 
 <template>
-  <div
+  <div ref="PageMainRef"
     class="page-main relative m-4 flex flex-col bg-[var(--g-container-bg)] transition-background-color-300" :class="{
       'of-hidden': isCollaspe,
     }" :style="{
@@ -45,3 +45,6 @@ function unCollaspe() {
     </div>
   </div>
 </template>
+<style>
+
+</style>

+ 207 - 170
src/components/Table/index.vue

@@ -1,191 +1,228 @@
-
 <template>
-	<div class="container">
-		<div class="table_box">
+  <div class="containerTable">
+    <div class="table_box">
       <div class="table-title-btn">
         <slot name="table-title-btn"></slot>
       </div>
-			<el-table ref="multipleTableRef"  :header-cell-style="{'background-color': '#f2f2f2'}" :data="props.tableData" style="width: 100%">
-				<el-table-column type="selection" v-if="showSelect" :selectable="selectable" width="55" />
-				
-				<el-table-column v-if="showIndex" type="index" label="序号" width="55" />
-				<el-table-column
-					v-for="column in columns"
-					:key="column.prop"
-					:prop="column.prop"
-					:label="column.label"
-					:sortable="column.sortable ? 'custom' : false"
-					:width="column.width"
-				>
-					<template #header>
-						<slot name="search" :slotData="column"></slot>
-					</template>
-					<template #default="{ row }">
-						<template v-if="row._edit">
-							<el-input
-								v-if="
-									column.prop === 'Date' || column.prop === 'Name' || column.prop === 'Organization'
-								"
-								v-model="row[column.prop]"
-							/>
-							<el-select v-if="column.prop === 'Tag'" v-model="Tagvalue">
-								<el-option
-									v-for="(type, label) in tagTypes"
-									:key="label"
-									:label="label"
-									:value="label"
-								/>
-							</el-select>
-						</template>
-						<template v-else>
-							<span v-if="column.prop !== 'Tag'">{{ row[column.prop] }}</span>
-							<el-tag v-if="column.prop === 'Tag'" :type="getTagType(row[column.prop])">{{
-								row[column.prop]
-							}}</el-tag>
-						</template>
-					</template>
-				</el-table-column>
-				<el-table-column label="操作">
-                    <template #default="scope">
-                        <slot name="operation" :operationData="scope"></slot>
-                    </template>
-					<!-- <template #default="scope">
+      <el-table ref="multipleTableRef"
+        :header-cell-style="{ 'background-color': '#EEF1F6', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }"
+         :data="props.tableData" :height="tableHeight" table-layout="fixed" style="width: 100%;"
+        @select="selectChange" @select-all="selectAllChange">
+        <el-table-column type="selection" v-if="showSelect" :selectable="selectable" width="55" />
+
+        <el-table-column v-if="showIndex" type="index" label="序号" width="55" />
+        <el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"
+          :sortable="column.sortable ? 'custom' : false" :width="column.width">
+
+          <template #header>
+            <slot name="search" :slotData="column"></slot>
+          </template>
+          <template #default="scope">
+            <component v-if="column.formatter"
+              :is="column.formatter(scope.row, column, scope.row[column.prop], scope.$index)" class="routerlick" />
+            <span v-else>{{ scope.row[column.prop] }}</span>
+          </template>
+          <!-- <template #default="{ row }">
+            <template v-if="row._edit">
+              <el-input v-if="
+                column.prop === 'Date' || column.prop === 'Name' || column.prop === 'Organization'
+              " v-model="row[column.prop]" />
+              <el-select v-if="column.prop === 'Tag'" v-model="Tagvalue">
+                <el-option v-for="(type, label) in tagTypes" :key="label" :label="label" :value="label" />
+              </el-select>
+            </template>
+            <template v-else>
+              <span v-if="column.prop !== 'Tag'">{{ row[column.prop] }}</span>
+              <el-tag v-if="column.prop === 'Tag'" :type="getTagType(row[column.prop])">{{
+                row[column.prop]
+              }}</el-tag>
+            </template>
+          </template> -->
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" :width="props.columnheight">
+          <template #default="scope">
+            <slot name="operation" :operationData="scope.row"></slot>
+          </template>
+          <!-- <template #default="scope">
 						<div v-if="scope.row._edit">
 							<el-button type="primary" @click="handleSave(scope)"> 保存 </el-button>
 							<el-button @click="handleCancle(scope)"> 取消 </el-button>
 						</div>
 						<el-button v-else type="primary" @click="handleEdit(scope)"> 编辑 </el-button>
 					</template> -->
-				</el-table-column>
-			</el-table>
-		</div>
-		<div class="page_box">
-			<el-pagination
-                v-model:current-page="props.pageInfo.pageNum"
-                v-model:page-size="props.pageInfo.pageSize"
-                :page-sizes="props.pageInfo.sizes"
-                layout="total, sizes, prev, pager, next, jumper"
-                :total="props.pageInfo.total"
-                @size-change="handleSizeChange"
-                @current-change="handleCurrentChange"
-            />
-		</div>
-	</div>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="page_box">
+      <el-pagination v-model:current-page="props.pageInfo.pageNum" v-model:page-size="props.pageInfo.pageSize"
+        :page-sizes="props.pageInfo.sizes" layout="total, sizes, prev, pager, next, jumper"
+        :total="props.pageInfo.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
+    </div>
+  </div>
 </template>
- 
+
 <script lang="ts" setup>
-	import { defineProps, ref, onMounted,defineEmits  } from "vue";
-	import type { TableInstance } from "element-plus";
- 
-	interface TableRow {
-		[key: string]: any; 
-	}
-    interface TagTypes{
-        [key:string]:string;
-    }
-    const emit = defineEmits(['getList']);
-	const props = defineProps<{
-		tableData: Array<TableRow>;
-		columns: {
-			prop: string;
-			label: string;
-			sortable?: boolean;
-			width?: string;
-			style?: string;
-		}[];
-        pageInfo:{
-            pageNum: number;
-            pageSize: number;
-            sizes: number[];
-            total: number;
-        };
-        showSelect:boolean;
-        showIndex:boolean;
-
-	}>();
-	const multipleTableRef = ref<TableInstance>();
-	const TableData = ref<TableRow[]>();
-	onMounted(() => {
-		let counter = 1;
-		TableData.value = props.tableData.map((row) => ({
-			...row,
-			_edit: false, // 设置默认非编辑状态
-			counter: counter++
-		}));
-	});
-	const selectable = () => true;
-	const Tagvalue = ref("");
-	const tagTypes:TagTypes = {
-		'公司': "primary",
-		'甲方': "success",
-		'合作伙伴': "primary"
-	};
-    const handleSizeChange = (val: number) => {
-        
-        props.pageInfo.pageSize=val
-      
-        emit('getList', props.pageInfo);
-    }
-    const handleCurrentChange = (val: number) => {
-   
-        props.pageInfo.pageNum=val
-        emit('getList', props.pageInfo);
+import { defineProps, ref, onMounted, defineEmits } from "vue";
+import type { TableInstance } from "element-plus";
 
+interface TableRow {
+  [key: string]: any;
+}
+interface TagTypes {
+  [key: string]: string;
+}
+const emit = defineEmits(['getList', 'selectAllChange','selectChange']);
+const props = defineProps<{
+  tableData: Array<TableRow>;
+  columnheight: number,
+  columns: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    formatter?: (row: any, column: any, cellValue: any, index: number) => any;
 
+  }[];
+  pageInfo: {
+    pageNum: number;
+    pageSize: number;
+    sizes: number[];
+    total: number;
+  };
+  showSelect: boolean;
+  showIndex: boolean;
+  tableheight?: (number | string),
+}>();
+const tableHeight = ref(props.tableheight); // 初始化表格高度
+const newSelectionList = ref<string[]>([]);
+const multipleTableRef = ref<TableInstance>();
+const TableData = ref<TableRow[]>();
+onMounted(() => {
+  let counter = 1;
+  TableData.value = props.tableData.map((row) => ({
+    ...row,
+    _edit: false, // 设置默认非编辑状态
+    counter: counter++
+
+  }));
+  setTimeout(() => {
+    const tableElement = multipleTableRef.value?.$el as HTMLElement;
+    if (tableElement) {
+      const offsetTop = tableElement.offsetTop; // 获取 offsetTop
+      let height = window.innerHeight;
+      console.log(height)
+      tableHeight.value = height - offsetTop - 270 + "px";
     }
-	const getTagType = (tag: string) => {
-		return tagTypes[tag] || tagTypes["公司"];
-	};
-	const handleEdit = (scope: { row: TableRow }) => {
-		Tagvalue.value = scope.row.Tag || "";
-		scope.row._edit = true;
-	};
-	const handleSave = (scope: { row: TableRow }) => {
-		scope.row.Tag = Tagvalue.value;
-		scope.row._edit = false;
-	};
-	const handleCancle = (scope: { row: TableRow }) => {
-		scope.row._edit = false;
-	};
-  const headClass= (scope: { row: TableRow }) =>  {
-      // return 'background:#F8F8F9'
-    }
+  }, 10);
+
+
+});
+const selectable = () => true;
+const Tagvalue = ref("");
+const tagTypes: TagTypes = {
+  '公司': "primary",
+  '甲方': "success",
+  '合作伙伴': "primary"
+};
+const handleSizeChange = (val: number) => {
+
+  props.pageInfo.pageSize = val
+
+  emit('getList', props.pageInfo);
+}
+//多选
+
+const selectAllChange = (value: any[]) => {
+  if (value.length>0) {
+    value.map(val => {
+      newSelectionList.value.push(val.id)
+    })
+  } else {
+    newSelectionList.value = [];
+  }
+  emit('selectAllChange', newSelectionList.value)
+
+}
+const selectChange = (value: any[]) => {
+  if (value.length>0) {
+    value.map(val => {
+      newSelectionList.value.push(val.id)
+    })
+  } else {
+    newSelectionList.value = [];
+  }
+  emit('selectChange', newSelectionList.value)
+}
+const handleCurrentChange = (val: number) => {
+
+  props.pageInfo.pageNum = val
+  emit('getList', props.pageInfo);
+
+
+}
+const getTagType = (tag: string) => {
+  return tagTypes[tag] || tagTypes["公司"];
+};
+const handleEdit = (scope: { row: TableRow }) => {
+  Tagvalue.value = scope.row.Tag || "";
+  scope.row._edit = true;
+};
+const handleSave = (scope: { row: TableRow }) => {
+  scope.row.Tag = Tagvalue.value;
+  scope.row._edit = false;
+};
+const handleCancle = (scope: { row: TableRow }) => {
+  scope.row._edit = false;
+};
+const headClass = (scope: { row: TableRow }) => {
+  // return 'background:#F8F8F9'
+}
 </script>
 <style lang="scss" scoped>
-	.container {
-		width: 100%;
-		height: auto;
-		margin: 5px;
-		display: flex;
-		flex-direction: column;
-	}
- .table_box {
-		flex: 1;
-		margin-bottom: 10px;
-		overflow-y: auto;
-		.table_item_container {
-			margin-left: 20px;
-			padding: 0;
-			.table_item {
-				margin: 5px 0;
-			}
-		}
-    :deep(.cell){
-      display: flex;
-      align-items: center;
-      justify-content: center;
-    }
-    .table-title-btn {
-      margin-bottom: 20px;
-      :deep(.el-button){
-        margin-right: 20px;
-      }
+.el-table .el-table__cell .routerlick > a {
+  color: red;
+}
+
+.containerTable {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: auto;
+}
+
+.table_box {
+  flex: 1;
+  margin-bottom: 10px;
+  overflow-y: auto;
+
+  .table_item_container {
+    padding: 0;
+    margin-left: 20px;
+
+    .table_item {
+      margin: 5px 0;
     }
-    
-	}
-  .page_box{
+  }
+
+  :deep(.cell) {
     display: flex;
     align-items: center;
-    justify-content: flex-end;
+    justify-content: center;
   }
-</style>
+
+  .table-title-btn {
+    margin-bottom: 20px;
+
+    :deep(.el-button) {
+      margin-right: 20px;
+    }
+  }
+}
+
+.page_box {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

+ 208 - 0
src/components/TableTree/index.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="containerTable">
+    <div class="table_box">
+      <div class="table-title-btn">
+        <slot name="table-title-btn"></slot>
+      </div>
+      <el-table ref="TableTreeRef" class="custom-table"
+        :header-cell-style="{ 'background-color': '#EEF1F6', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }"
+        :data="props.tableData" :height="tableHeight" table-layout="fixed" row-key="id" default-expand-all row-class-name="cellClass"
+        style="width: 100%;">
+        <el-table-column type="selection" v-if="showSelect" :selectable="selectable" width="55" />
+        <el-table-column v-if="showIndex" type="index" label="序号" width="55" />
+        <el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"
+          :sortable="column.sortable ? 'custom' : false" :width="column.width">
+          <template #default="scope">
+            <component v-if="column.formatter"
+              :is="column.formatter(scope.row, column, scope.row[column.prop], scope.$index)" class="routerlick" />
+            <span v-else>{{ scope.row[column.prop] }}</span>
+          </template>
+          <template #header>
+            <slot name="search" :slotData="column"></slot>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" :width="props.columnheight">
+          <template #default="scope">
+            <slot name="operation" class="operationClass" :operationData="scope.row"></slot>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { defineProps, ref, onMounted, defineEmits } from "vue";
+import type { TableInstance } from "element-plus";
+
+interface TableRow {
+  [key: string]: any;
+}
+interface TagTypes {
+  [key: string]: string;
+}
+const emit = defineEmits([]);
+const props = defineProps<{
+  tableData: Array<TableRow>;
+  columnheight: number,
+  columns: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    formatter?: (row: any, column: any, cellValue: any, index: number) => any;
+
+  }[];
+  showSelect: boolean;
+  showIndex: boolean;
+  tableheight?: (number | string),
+}>();
+const tableHeight = ref(props.tableheight); // 初始化表格高度
+
+const TableTreeRef = ref<TableInstance>();
+const TableData = ref<TableRow[]>();
+onMounted(() => {
+  let counter = 1;
+  TableData.value = props.tableData.map((row) => ({
+    ...row,
+    _edit: false, // 设置默认非编辑状态
+    counter: counter++
+
+  }));
+  setTimeout(() => {
+    const tableElement = TableTreeRef.value?.$el as HTMLElement;
+    if (tableElement) {
+      const offsetTop = tableElement.offsetTop; // 获取 offsetTop
+      let height = window.innerHeight;
+      console.log(height)
+      tableHeight.value = height - offsetTop - 300 + "px";
+    }
+  }, 10);
+
+
+});
+const selectable = () => true;
+const Tagvalue = ref("");
+const tagTypes: TagTypes = {
+  '公司': "primary",
+  '甲方': "success",
+  '合作伙伴': "primary"
+};
+
+//多选
+
+
+const getTagType = (tag: string) => {
+  return tagTypes[tag] || tagTypes["公司"];
+};
+const handleEdit = (scope: { row: TableRow }) => {
+  Tagvalue.value = scope.row.Tag || "";
+  scope.row._edit = true;
+};
+const handleSave = (scope: { row: TableRow }) => {
+  scope.row.Tag = Tagvalue.value;
+  scope.row._edit = false;
+};
+const handleCancle = (scope: { row: TableRow }) => {
+  scope.row._edit = false;
+};
+const headClass = (scope: { row: TableRow }) => {
+  // return 'background:#F8F8F9'
+}
+</script>
+<style lang="scss" scoped>
+.el-table .el-table__cell .routerlick > a {
+  color: red;
+}
+
+.containerTable {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: auto;
+}
+
+.table_box {
+  flex: 1;
+  margin-bottom: 10px;
+  overflow-y: auto;
+
+  .table_item_container {
+    padding: 0;
+    margin-left: 20px;
+
+    .table_item {
+      margin: 5px 0;
+    }
+  }
+
+  :deep(.cell) {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  .table-title-btn {
+    margin-bottom: 20px;
+
+    :deep(.el-button) {
+      margin-right: 20px;
+    }
+  }
+}
+
+.page_box {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+
+/* 自定义树状表格图标 */
+
+/* 有子节点 且未展开 */
+.custom-table :deep(.el-table__expand-icon) {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 18px;
+  height: 18px;
+  background: url("../../assets/icons/caretRight.svg") no-repeat;
+  background-size: auto;
+
+  svg {
+    display: none;
+  }
+}
+
+/* 有子节点 且已展开 */
+.custom-table :deep(.el-table__expand-icon--expanded) {
+  display: inline-block;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 18px;
+  height: 18px;
+  content: "";
+  background: url("../../assets/icons/caretRight.svg") no-repeat;
+  background-size: auto;
+  transform: rotate(90deg);
+
+  svg {
+    display: none;
+  }
+}
+
+:deep(.el-table) {
+  .el-table__row--level-1 {
+    background-color: #eef1f6;
+  }
+}
+
+.operationClass {
+  .el-button {
+    margin: 0 7px;
+  }
+}
+</style>

+ 19 - 13
src/layouts/components/Header/index.vue

@@ -34,18 +34,18 @@ function handlerMouserScroll(event: WheelEvent) {
           <!-- 顶部模式 -->
           <div class="menu h-full flex of-hidden transition-all">
             <template v-for="(item, index) in menuStore.allMenus" :key="index">
-              <div
-                class="menu-item relative mx-1 py-2 transition-all" :class="{
-                  active: index === menuStore.actived,
-                }"
-              >
-                <div
-                  v-if="item.children && item.children.length !== 0" class="group menu-item-container h-full w-full flex cursor-pointer items-center justify-between gap-1 px-3 text-[var(--g-header-menu-color)] transition-all hover-(bg-[var(--g-header-menu-hover-bg)] text-[var(--g-header-menu-hover-color)])" :class="{
+              <div class="menu-item relative mx-1 py-2 transition-all" :class="{
+                active: index === menuStore.actived,
+              }">
+                <div v-if="item.children && item.children.length !== 0"
+                  class="group menu-item-container h-full w-full flex cursor-pointer items-center justify-between gap-1 px-3 text-[var(--g-header-menu-color)] transition-all hover-(bg-[var(--g-header-menu-hover-bg)] text-[var(--g-header-menu-hover-color)])"
+                  :class="{
                     'text-[var(--g-header-menu-active-color)]! bg-[var(--g-header-menu-active-bg)]!': index === menuStore.actived,
-                  }" :title="typeof item.meta?.title === 'function' ? item.meta?.title() : item.meta?.title" @click="switchTo(index)"
-                >
+                  }" :title="typeof item.meta?.title === 'function' ? item.meta?.title() : item.meta?.title"
+                  @click="switchTo(index)">
                   <div class="inline-flex flex-1 items-center justify-center gap-1">
-                    <SvgIcon v-if="item.meta?.icon" :name="item.meta?.icon" class="menu-item-container-icon transition-transform group-hover-scale-120" />
+                    <SvgIcon v-if="item.meta?.icon" :name="item.meta?.icon"
+                      class="menu-item-container-icon transition-transform group-hover-scale-120" />
                     <span class="w-full flex-1 truncate text-sm transition-height transition-opacity transition-width">
                       {{ typeof item.meta?.title === 'function' ? item.meta?.title() : item.meta?.title }}
                     </span>
@@ -87,6 +87,15 @@ header {
     width: 100%;
     height: 100%;
     margin: 0 auto;
+    background-image:
+      url("../../../assets/images/decoration1.png"),
+      url("../../../assets/images/decoration2.png"),
+      url("../../../assets/images/decoration3.png");
+    background-repeat: no-repeat, no-repeat, no-repeat;
+    background-position:
+      left center,
+      center center,
+      calc(100% - 84px) center;
 
     :deep(a.title) {
       position: relative;
@@ -113,9 +122,6 @@ header {
       height: 100%;
       padding: 0 20px;
       overflow-x: auto;
-      background-image: url("../../../assets/images/decoration1.png");
-      background-repeat: no-repeat; /* 防止背景图片重复 */
-      background-position: center; /* 将背景图片居中 */
       mask-image: linear-gradient(to right, transparent, #000 20px, #000 calc(100% - 20px), transparent);
 
       .menu {

+ 90 - 68
src/layouts/components/HotkeysIntro/index.vue

@@ -1,88 +1,110 @@
 <script setup lang="ts">
-import useSettingsStore from '@/store/modules/settings'
+import { reactive, ref } from 'vue'
 import eventBus from '@/utils/eventBus'
+import { FormInstance, FormRules, ElMessage } from 'element-plus'
+import api from '@/api'
+import useUserStore from '@/store/modules/user'
+const userStore = useUserStore()
 
 defineOptions({
   name: 'HotkeysIntro',
 })
 
+interface RuleForm {
+  oldPassWord: string,
+  newPassWord: string,
+}
+const formLabelAlign = reactive({
+  oldPassWord: '',//旧密码
+  newPassWord: '',//新密码
+})
+const ruleFormRef = ref<FormInstance>()
+const newPassWords = ref();
 const isShow = ref(false)
-
-const settingsStore = useSettingsStore()
-
+const formrules = reactive<FormRules<RuleForm>>({
+  oldPassWord: [
+    { required: true, message: '旧密码不能为空', trigger: 'blur' },
+  ],
+  newPassWord: [
+    { required: true, message: '新密码不能为空', trigger: 'blur' },
+    { min: 8, max: 20, message: '8-20位字符,包含字母和数字', trigger: 'blur' },
+  ],
+})
 onMounted(() => {
   eventBus.on('global-hotkeys-intro-toggle', () => {
     isShow.value = !isShow.value
   })
 })
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid, fields): any => {
+    if (valid) {
+      if (formLabelAlign.oldPassWord == formLabelAlign.newPassWord) {
+        return ElMessage({
+          message: '输入的旧密码和新密码一致',
+          type: 'warning',
+          plain: true,
+        })
+      }
+      if (newPassWords.value !== formLabelAlign.newPassWord) {
+        return ElMessage({
+          message: '两次输入的新密码不一致',
+          type: 'warning',
+          plain: true,
+        })
+      }
+      api.user.passwordEdit(formLabelAlign).then((res: any) => {
+        if (res.code == 200) {
+          isShow.value=false;
+          ElMessage({
+            message: res.msg,
+            type: 'success',
+            plain: true,
+          })
+          setTimeout(() => {
+            userStore.logout();
+          }, 1000)
+        }
+      })
+    }
+  })
+}
 </script>
 
 <template>
-  <HDialog v-model="isShow" title="快捷键介绍">
-    <div class="px-4">
-      <div class="grid gap-2 sm-grid-cols-2">
-        <div>
-          <h2 class="m-0 text-lg font-bold">
-            全局
-          </h2>
-          <ul class="list-none pl-4 text-sm">
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>I</HKbd>
-              查看系统信息
-            </li>
-            <li v-if="settingsStore.settings.toolbar.navSearch && settingsStore.settings.navSearch.enableHotkeys" class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>S</HKbd>
-              唤起导航搜索
-            </li>
-          </ul>
-        </div>
-        <div v-if="settingsStore.settings.menu.enableHotkeys && ['side', 'head'].includes(settingsStore.settings.menu.mode)">
-          <h2 class="m-0 text-lg font-bold">
-            主导航
-          </h2>
-          <ul class="list-none pl-4 text-sm">
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>`</HKbd>
-              激活下一个主导航
-            </li>
-          </ul>
-        </div>
-        <div v-if="settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.enableHotkeys">
-          <h2 class="m-0 text-lg font-bold">
-            标签栏
-          </h2>
-          <ul class="list-none pl-4 text-sm">
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>←</HKbd>
-              切换到上一个标签页
-            </li>
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>→</HKbd>
-              切换到下一个标签页
-            </li>
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>W</HKbd>
-              关闭当前标签页
-            </li>
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>1~9</HKbd>
-              切换到第 n 个标签页
-            </li>
-            <li class="py-1">
-              <HKbd>{{ settingsStore.os === 'mac' ? '⌥' : 'Alt' }}</HKbd>
-              <HKbd>0</HKbd>
-              切换到最后一个标签页
-            </li>
-          </ul>
-        </div>
+
+  <HDialog v-model="isShow" title="修改密码">
+    <div style="padding: 0 20px;" class="">
+      <el-form label-position="top" label-width="auto" :rules="formrules" size="large" ref="ruleFormRef"
+        :model="formLabelAlign">
+        <el-form-item label="请输入旧密码" prop="oldPassWord">
+          <el-input v-model="formLabelAlign.oldPassWord" type="password" show-password />
+        </el-form-item>
+        <el-form-item label="输入新密码" prop="newPassWord">
+          <el-input v-model="formLabelAlign.newPassWord" type="password" show-password />
+        </el-form-item>
+        <el-form-item label="再次输入新密码">
+          <el-input v-model="newPassWords" type="password" show-password />
+        </el-form-item>
+      </el-form>
+      <h6 style="color: #666;">8-20位字符,包含字母和数字,建议字母包含大小写组合。</h6>
+      <div class="dialog-footer flex a-c j-s">
+        <el-button @click="isShow = false" size="large">取消</el-button>
+        <el-button type="primary" style="background: linear-gradient(91deg, #4fcaff 0%, #2b62ff 100%);border: none;"
+          @click="submitForm(ruleFormRef)" size="large">
+          确认修改
+        </el-button>
       </div>
     </div>
+
   </HDialog>
 </template>
+<style lang="scss" scoped>
+.dialog-footer {
+  width: 100%;
+
+  .el-button {
+    width: 45%;
+  }
+}
+</style>

+ 7 - 3
src/layouts/components/Logo/index.vue

@@ -20,14 +20,18 @@ withDefaults(
 const settingsStore = useSettingsStore()
 
 const title = ref(import.meta.env.VITE_APP_TITLE)
-const logo = ref(imgLogo)
+// const logo = ref(imgLogo)
 
 const to = computed(() => settingsStore.settings.home.enable ? settingsStore.settings.home.fullPath : '')
 </script>
 
 <template>
   <RouterLink :to="to" class="h-[var(--g-sidebar-logo-height)] w-inherit flex-center gap-2 px-3 text-inherit no-underline" :class="{ 'cursor-pointer': settingsStore.settings.home.enable }" :title="title">
-    <img v-if="showLogo" :src="logo" class="logo h-[30px] w-[30px] object-contain">
-    <span v-if="showTitle" class="block truncate font-bold">{{ title }}</span>
+    <!-- 框架原始代码 -->
+    <!-- <img v-if="showLogo" :src="logo" class="logo h-[30px] w-[30px] object-contain">
+    <span v-if="showTitle" class="block truncate font-bold">{{ title }}</span> -->
+    <!-- 自定义内容 -->
+    <img v-if="showLogo" src="../../../assets/images/titleImg.png" class="logo h-[30px] w-[30px] object-contain">
+
   </RouterLink>
 </template>

+ 3 - 0
src/layouts/components/Menu/sub.vue

@@ -202,3 +202,6 @@ function handleMouseleave() {
     </Transition>
   </Teleport>
 </template>
+<style>
+
+</style>

+ 126 - 20
src/layouts/components/Topbar/Toolbar/rightSide.vue

@@ -6,22 +6,60 @@ import ColorScheme from './ColorScheme/index.vue'
 import Fullscreen from './Fullscreen/index.vue'
 import NavSearch from './NavSearch/index.vue'
 import PageReload from './PageReload/index.vue'
+import { getFirstLetterPinyin } from '@/utils/composables/pinyinHelper';
+import api from '@/api'
+onMounted(() => {
+  convert();
 
+})
 defineOptions({
   name: 'Tools',
 })
-
-const router = useRouter()
-
 const settingsStore = useSettingsStore()
 const userStore = useUserStore()
-
+const inputText = ref(userStore.userName); // 用户输入的中文文本
+const firstLetterPinyin = ref(''); // 转换为拼音首字母
+console.log(userStore.avatar)
+//获取
+const convert = () => {
+  firstLetterPinyin.value = getFirstLetterPinyin(inputText.value);
+};
 const avatarError = ref(false)
 watch(() => userStore.avatar, () => {
   if (avatarError.value) {
     avatarError.value = false
   }
 })
+const fileInput = ref()
+
+const changeAvatar = () => {
+  fileInput.value.click()//调用输入框事件
+}
+//自定义选择文件
+const handleFileChange = (event: any) => {
+  const file = event.target.files[0]
+  console.log(file)
+  if (file) {
+    const params = new FormData(); // 声明formData数据类型
+    params.append("file", file);
+    api.toolApi.fileupload(params).then((res: any) => {
+      if (res.code == 200) {
+        let imgid = res.data.id;//图片id
+        //调用修改头像接口
+        imgupdate(imgid);
+      }
+    })
+  }
+}
+const imgupdate = (id: string) => {
+  api.user.changeHeadSculpture({ id: id }).then((res: any) => {
+    if (res.code == '200') {
+      localStorage.setItem('avatar', res.msg)
+      window.location.reload();
+    } else {
+    }
+  });
+}
 </script>
 
 <template>
@@ -30,25 +68,93 @@ watch(() => userStore.avatar, () => {
     <Fullscreen v-if="settingsStore.settings.toolbar.fullscreen" />
     <PageReload v-if="settingsStore.settings.toolbar.pageReload" />
     <ColorScheme v-if="settingsStore.settings.toolbar.colorScheme" />
-    <HDropdownMenu
-      :items="[
-        [
-          { label: settingsStore.settings.home.title, handle: () => router.push({ path: settingsStore.settings.home.fullPath }), hide: !settingsStore.settings.home.enable },
-        ],
-        [
-          { label: '快捷键介绍', handle: () => eventBus.emit('global-hotkeys-intro-toggle'), hide: settingsStore.mode !== 'pc' },
-        ],
-        [
-          { label: '退出登录', handle: () => userStore.logout(settingsStore.settings.home.fullPath) },
-        ],
-      ]" class="flex-center cursor-pointer px-2"
-    >
+    <HDropdownMenu :items="[
+      [
+        { label: '修改密码', handle: () => eventBus.emit('global-hotkeys-intro-toggle'), hide: settingsStore.mode !== 'pc', icon: 'Unlock' },
+      ],
+      [
+        { label: '退出登录', icon: 'SwitchButton', handle: () => userStore.logout(settingsStore.settings.home.fullPath) },
+      ],
+    ]" class="flex-center cursor-pointer px-2">
+      <template v-slot:header>
+        <div class="userInfo flex j-s a-c">
+          <input type="file" ref="fileInput" @change="handleFileChange" accept="image/*" style="display: none;" />
+          <div class="defaultAvatar flex a-c j-c" @click="changeAvatar">
+            <img v-if="userStore.avatar && !avatarError" :src="userStore.avatar" alt=""
+              class="h-[24px] w-[24px] rounded-full">
+            <img v-else :src="userStore.selectedImage" alt="" class="h-[24px] w-[24px] rounded-full">
+            <span class="imgAeg">更换</span>
+          </div>
+          <div class="flex f-c j-s info">
+            <span> {{ userStore.realName }}</span>
+            <span> {{ userStore.userName }}</span>
+          </div>
+        </div>
+      </template>
       <div class="flex-center gap-1">
-        <img v-if="userStore.avatar && !avatarError" :src="userStore.avatar" :onerror="() => (avatarError = true)" class="h-[24px] w-[24px] rounded-full">
-        <SvgIcon v-else name="i-carbon:user-avatar-filled-alt" :size="24" class="text-gray-400" />
-        {{ userStore.userName }}
+        <div v-if="userStore.avatar && !avatarError" style="position: relative;">
+          <img :src="userStore.avatar" :onerror="() => (avatarError = true)" class="h-[24px] w-[24px] rounded-full"
+            style="margin-top: 5px;">
+        </div>
+        <div v-else class="defaultAvatar">
+          <img :src="userStore.selectedImage" :onerror="() => (avatarError = true)"
+            class="h-[24px] w-[24px] rounded-full" style="margin-top: 5px;">
+          <span class="imgAeg" style="font-size: 12px;">{{ firstLetterPinyin }}</span>
+        </div>
+        {{ userStore.realName }}
         <SvgIcon name="i-ep:caret-bottom" />
       </div>
     </HDropdownMenu>
   </div>
 </template>
+<style lang="scss" scoped>
+.userInfo {
+  padding: 11px 16px;
+  margin-right: 8px;
+  border: 1px solid #eee;
+
+  img {
+    width: 40px;
+    height: 40px;
+  }
+
+  .defaultAvatar {
+    position: relative;
+    cursor: pointer;
+
+    .imgAeg {
+      position: absolute;
+      font-size: 9px;
+      color: #fff;
+    }
+  }
+
+  .info {
+    margin-left: 8px;
+
+    span:first-child {
+      margin-bottom: 8px;
+      font-size: 16px;
+      font-weight: bold;
+      color: #333;
+    }
+
+    span:last-child {
+      font-size: 14px;
+      color: #666;
+    }
+  }
+}
+
+.defaultAvatar {
+  position: relative;
+
+  .imgAeg {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    color: #fff;
+    transform: translate(-50%, -50%);
+  }
+}
+</style>

+ 10 - 4
src/layouts/ui-kit/HDropdownMenu.vue

@@ -4,6 +4,7 @@ const props = defineProps<{
     label: string
     disabled?: boolean
     hide?: boolean
+    icon?:string,
     handle?: () => void
   }[][]
 }>()
@@ -16,13 +17,18 @@ const myItems = computed(() => {
 </script>
 
 <template>
-  <VMenu :show-triggers="['hover']" :auto-hide="false" :popper-triggers="['hover', 'click']" :delay="200" v-bind="$attrs">
+  <VMenu :show-triggers="['hover']" :auto-hide="false" :popper-triggers="['hover', 'click']" :delay="200"
+    v-bind="$attrs">
     <slot />
     <template #popper>
-      <div v-for="(item, index) in myItems" :key="index" class="b-b-(1 stone-2 solid) p-1 dark-b-b-(1 stone-7) last-b-b-size-0">
-        <button v-for="(v, i) in item" :key="i" :disabled="v.disabled" class="w-full flex cursor-pointer items-center gap-2 border-size-0 rounded-md bg-inherit px-2 py-1.5 text-sm text-dark disabled-cursor-not-allowed dark-text-white disabled-opacity-50 hover-not-disabled-bg-stone-1 dark-hover-not-disabled-bg-stone-9" @click="v.handle">
+      <slot name="header"></slot>
+      <div v-for="(item, index) in myItems" :key="index"
+        class="b-b-(1 stone-2 solid) p-1 dark-b-b-(1 stone-7) last-b-b-size-0 flex a-c " style="padding: 10px 16px;">
+        <el-button v-for="(v, i) in item" :key="i" :disabled="v.disabled"
+          class="w-full flex cursor-pointer a-c j-start gap-2 border-size-0 rounded-md bg-inherit px-2 py-1.5 text-sm text-dark disabled-cursor-not-allowed dark-text-white disabled-opacity-50 hover-not-disabled-bg-stone-1 dark-hover-not-disabled-bg-stone-9"
+          @click="v.handle" :icon="v.icon">
           {{ v.label }}
-        </button>
+        </el-button>
       </div>
     </template>
   </VMenu>

+ 0 - 37
src/router/modules/multilevel.menu.example.ts

@@ -1,37 +0,0 @@
-import type { RouteRecordRaw } from 'vue-router'
-
-const Layout = () => import('@/layouts/index.vue')
-
-const routes: RouteRecordRaw = {
-  path: '/example',
-  component: Layout,
-  redirect: '/example/index',
-  name: 'Example',
-  meta: {
-    title: '演示',
-    type: 0,
-  },
-  children: [
-    {
-      path: 'index',
-      name: 'ExampleIndex',
-      component: () => import('@/views/example/index.vue'),
-      meta: {
-        title: '演示页面',
-        Type: 1,
-      },
-    },
-    {
-      path: 'ceshi',
-      name: 'Exampleceshi',
-      component: () => import('@/views/example/ceshi.vue'),
-      meta: {
-        title: '权限测试',
-        Type: 1,
-        
-      },
-    },
-  ],
-}
-
-export default routes

+ 60 - 9
src/router/modules/organization.ts

@@ -3,24 +3,75 @@ import type { RouteRecordRaw } from 'vue-router'
 const Layout = () => import('@/layouts/index.vue')
 
 const routes: RouteRecordRaw = {
-  path: '/organizationManagement',
+  path: '/organizationManagement/organization',
   component: Layout,
-  redirect: '/organizationManagement/organization',
-  name: 'OrganizationManagement',
+  redirect: '/organizationManagement/organization/organizationlist',
+  name: 'OrganizationManagementorganization',
   meta: {
-    title: '机构管理',
+    title: '租户管理',
 
   },
   children: [
     {
-      path: 'organization',
-      name: 'Organization',
-      component: () => import('@/views/organizationManagement/organization.vue'),
+      path: 'organizationlist',
+      name: 'Organizationlist',
+      redirect: '/organizationManagement/organization/organizationlist',
       meta: {
-        title: '机构管理',
+        title: '租户列表',
       },
-    },
+      children: [
+        {
+          path: '',
+          component: () => import('@/views/organizationManagement/organization/organizationlist.vue'),
+          meta: {
+            title: '租户列表',
+            menu: false,
+            breadcrumb: false,
+        auth:['organizationlist.add','organizationlist.view','organizationlist.delete','organizationlist.edit']
 
+          },
+        },
+        {
+          path: 'organizationAdd',
+          name: 'OrganizationAdd',
+          component: () => import('@/views/organizationManagement/organization/organizationAdd.vue'),
+          meta: {
+            title: '租户添加',
+            menu: false,
+            activeMenu: '/organizationManagement/organization/organizationlist',
+          },
+        },
+        {
+          path: 'organizationDetail',
+          name: 'OrganizationDetail',
+          component: () => import('@/views/organizationManagement/organization/organizationDetail.vue'),
+          meta: {
+            title: '租户详情',
+            menu: false,
+            activeMenu: '/organizationManagement/organization/organizationlist',
+          },
+        },
+        {
+          path: 'deptAdd',
+          name: 'OrganizationDeptAdd',
+          component: () => import('@/views/organizationManagement/organization/subOrganizatioAdd.vue'),
+          meta: {
+            title: '添加子机构',
+            menu: false,
+            activeMenu: '/organizationManagement/organization/organizationlist',
+          },
+        },
+      ],
+    },
+    {
+      path: 'tenantPermissions',
+      name: 'TenantPermissions',
+      component: () => import('@/views/organizationManagement/tenantPermissions.vue'),
+      meta: {
+        title: '租户权限',
+        auth:['tenantPermissions.add','tenantPermissions.view','tenantPermissions.delete','tenantPermissions.edit']
+      },
+    },
   ],
 }
 

+ 25 - 4
src/router/modules/personnel.ts

@@ -12,14 +12,35 @@ const routes: RouteRecordRaw = {
   },
   children: [
     {
-      path: 'member',
-      name: 'personnelMember',
-      component: () => import('@/views/personnel/memberManagement.vue'),
+      path: 'memberManagement',
+      name: 'MemberManagement',
+      redirect: '/personnel/memberManagement',
       meta: {
         title: '成员管理',
-
+        auth:['member.add','member.view','member.delete','member.edit']
 
       },
+      children: [
+        {
+          path: '',
+          component: () => import('@/views/personnel/memberManagement.vue'),
+          meta: {
+            title: '成员管理',
+            menu: false,
+            breadcrumb: false,
+          },
+        },
+        {
+          path: 'memberAdd',
+          name: 'MemberAdd',
+          component: () => import('@/views/personnel/memberAdd.vue'),
+          meta: {
+            title: '成员添加',
+            menu: false,
+            activeMenu: '/personnel/memberManagement',
+          },
+        },
+      ]
     },
     {
       path: 'role',

+ 0 - 12
src/router/routes.ts

@@ -3,7 +3,6 @@ import type { RouteRecordRaw } from 'vue-router'
 import useSettingsStore from '@/store/modules/settings'
 import generatedRoutes from 'virtual:generated-pages'
 import { setupLayouts } from 'virtual:meta-layouts'
-import MultilevelMenuExample from './modules/multilevel.menu.example'
 import organizationManagement from './modules/organization'
 import personnel from './modules/personnel'
 
@@ -61,23 +60,12 @@ const systemRoutes: RouteRecordRaw[] = [
 
 // 动态路由(异步路由、导航栏路由)
 const asyncRoutes: Route.recordMainRaw[] = [
-  {
-    meta: {
-      title: '演示',
-      icon: 'i-uim:box',
-    },
-    children: [
-      MultilevelMenuExample,
-
-    ],
-  },
   {
     meta: {
       title: '组织架构',
       icon: 'i-uim:box',
     },
     children: [
-      organizationManagement,
       personnel
     ],
   },

+ 1 - 1
src/settings.ts

@@ -31,7 +31,7 @@ const globalSettings: Settings.all = {
     enableHotkeys: false,
   },
   topbar: {
-    mode: 'static',
+    mode: 'fixed',
   },
   tabbar: {
     enable: true,

+ 1 - 2
src/store/modules/menu.ts

@@ -142,8 +142,7 @@ const useMenuStore = defineStore(
     function hasPermission(permissions: string[], menu: Menu.recordMainRaw | Menu.recordRaw) {
       let isAuth = false
       if (menu.meta?.auth) {
-        isAuth = permissions.some((auth) => {
-          console.log(auth)
+        isAuth = permissions?.some((auth) => {
           if (typeof menu.meta?.auth === 'string') {
             return menu.meta.auth !== '' ? menu.meta.auth === auth : true
           }

+ 32 - 14
src/store/modules/user.ts

@@ -4,6 +4,10 @@ import useRouteStore from './route'
 import useSettingsStore from './settings'
 import useTabbarStore from './tabbar'
 import api from '@/api'
+import { getRandomImage } from '@/utils/composables/imageUtils';
+
+// 使用函数
+
 const useUserStore = defineStore(
   // 唯一ID
   'user',
@@ -13,10 +17,12 @@ const useUserStore = defineStore(
     const menuStore = useMenuStore()
     const tabbarStore = useTabbarStore()
 
-    const userName = ref(localStorage.userName ?? '')
+    const userName = ref(localStorage.userName ?? '')//
+    const realName = ref(localStorage.realName ?? '')//
     const token = ref(localStorage.token ?? '')
     const avatar = ref(localStorage.avatar ?? '')
     const permissions = ref<string[]>([])
+    const selectedImage: string = getRandomImage();//随机切换默认头像
 
     const isLogin = computed(() => {
       if (token.value) {
@@ -32,22 +38,23 @@ const useUserStore = defineStore(
       system: string
       captchaId: number
     }) {
-        const res = await api.userApi.login(data)
-        console.log(res)
-        localStorage.setItem('userName', data.userName)
-        localStorage.setItem('token', res.data)
-        userName.value = data.userName
-        token.value = res.data
-        avatar.value = 'https://fantastic-admin.hurui.me/logo.png'
+      const res = await api.user.login(data)
+      localStorage.setItem('userName', data.userName)
+      localStorage.setItem('token', res.data)
+      userName.value = data.userName
+      token.value = res.data
+      if(token.value){
+        getUserInfo();
+      }
     }
     // 登出
     async function logout(redirect = router.currentRoute.value.fullPath) {
       localStorage.removeItem('userName')
+      localStorage.removeItem('realName')
       localStorage.removeItem('token')
-      localStorage.removeItem('avatar')
       userName.value = ''
+      realName.value = ''
       token.value = ''
-      // avatar.value = ''
       permissions.value = []
       routeStore.removeRoutes()
       menuStore.setActived(0)
@@ -59,29 +66,40 @@ const useUserStore = defineStore(
         },
       })
     }
+    //获取用户信息
+    async function getUserInfo() {
+      const usercallback=await api.user.getUserInfo();
+      localStorage.setItem('avatar', usercallback.data.headSculpture)
+      localStorage.setItem('realName', usercallback.data.name)
+      avatar.value=usercallback.data.headSculpture;
+      realName.value=usercallback.data.name;
+    }
     // 获取权限
     async function getPermissions() {
-      const res = await api.userApi.permission()
+      const res = await api.user.permission()
       permissions.value = res.data
     }
     // 修改密码
     async function editPassword(data: {
-      password: string
-      newpassword: string
+      oldPassWord: string
+      newPassWord: string
     }) {
-      await api.userApi.passwordEdit(data)
+      await api.user.passwordEdit(data)
     }
 
     return {
       userName,
+      realName,
       token,
       avatar,
       permissions,
       isLogin,
+      getUserInfo,
       login,
       logout,
       getPermissions,
       editPassword,
+      selectedImage,
     }
   },
 )

+ 3 - 0
src/types/auto-imports.d.ts

@@ -9,6 +9,7 @@ declare global {
   const EffectScope: typeof import('vue')['EffectScope']
   const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
   const computed: typeof import('vue')['computed']
+  const convertToPinyin: typeof import('../utils/composables/pinyinHelper')['convertToPinyin']
   const convertToTreeData: typeof import('../utils/composables/routeUtils')['convertToTreeData']
   const createApp: typeof import('vue')['createApp']
   const createPinia: typeof import('pinia')['createPinia']
@@ -20,6 +21,8 @@ declare global {
   const getActivePinia: typeof import('pinia')['getActivePinia']
   const getCurrentInstance: typeof import('vue')['getCurrentInstance']
   const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const getFirstLetterPinyin: typeof import('../utils/composables/pinyinHelper')['getFirstLetterPinyin']
+  const getRandomImage: typeof import('../utils/composables/imageUtils')['getRandomImage']
   const h: typeof import('vue')['h']
   const inject: typeof import('vue')['inject']
   const isProxy: typeof import('vue')['isProxy']

+ 1 - 0
src/types/components.d.ts

@@ -39,6 +39,7 @@ declare module 'vue' {
     SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
     SystemInfo: typeof import('./../components/SystemInfo/index.vue')['default']
     Table: typeof import('./../components/Table/index.vue')['default']
+    TableTree: typeof import('./../components/TableTree/index.vue')['default']
     Trend: typeof import('./../components/Trend/index.vue')['default']
   }
 }

+ 19 - 0
src/utils/composables/imageUtils.ts

@@ -0,0 +1,19 @@
+/**
+ * 随机获取四个图片中的某一个
+ * @returns 随机选中的图片URL或文件名
+ */
+export function getRandomImage(): string {
+  // 定义四个图片的URL或文件名
+  const images: string[] = [
+    '/src/assets/images/user/avatar1.png',
+    '/src/assets/images/user/avatar2.png',
+    '/src/assets/images/user/avatar3.png',
+    '/src/assets/images/user/avatar4.png',
+  ];
+
+  // 生成0到3之间的随机整数
+  const randomIndex: number = Math.floor(Math.random() * images.length);
+
+  // 返回随机选中的图片
+  return images[randomIndex];
+}

+ 30 - 0
src/utils/composables/pinyinHelper.ts

@@ -0,0 +1,30 @@
+import pinyin from 'pinyin';
+
+/**
+ * 将中文字符串转换为拼音
+ * @param text 中文字符串
+ * @param style 拼音风格
+ * @returns 拼音字符串
+ */
+export const convertToPinyin = (
+  text: string,
+  style: any = pinyin.STYLE_NORMAL
+): string => {
+  return pinyin(text, { style })
+    .map(item => item[0]) // 提取每个字的拼音
+    .join(' '); // 用空格连接拼音
+};
+
+/**
+ * 获取字符串第一个字的拼音首字母
+ * @param text 中文字符串
+ * @returns 第一个字的拼音首字母(大写)
+ */
+export const getFirstLetterPinyin = (text: string): string => {
+  if (!text) return '';
+
+  const firstChar = text.charAt(0);
+  const pinyinResult = pinyin(firstChar, { style: pinyin.STYLE_FIRST_LETTER });
+
+  return pinyinResult[0][0].toUpperCase();
+};

+ 0 - 15
src/views/example/ceshi.vue

@@ -1,15 +0,0 @@
-<script setup lang='ts'>
-</script>
-
-<!-- 模板区域 -->
-<template>
-  <div>
-    <!-- 模板内容 -->
-    <PageMain>
-      <h1>1</h1>
-    </PageMain>
-  </div>
-</template>
-
-<!-- 行为区域 -->
-<!-- 样式区域 -->

+ 0 - 52
src/views/example/index.vue

@@ -1,52 +0,0 @@
-<script setup lang='ts'>
-</script>
-
-<!-- 模板区域 -->
-<template>
-  <div>
-    <!-- <PageHeader title="内容块" content="PageMain" /> -->
-    <PageMain>
-
-    </PageMain>
-    <PageMain title="你可以设置一个自定义的标题">
-      这里放页面内容
-    </PageMain>
-    <PageMain>
-      <template #title>
-        <div style="display: flex; align-items: center; justify-content: space-between;">
-          通过 slot 设置标题
-          <ElButton size="small">
-            还可以放置自定义按钮
-          </ElButton>
-        </div>
-      </template>
-      这里放页面内容
-    </PageMain>
-      <ElRow :gutter="20" style="margin: -10px 10px;">
-      <ElCol :md="8">
-        <PageMain style="margin: 10px 0;">
-          还可以结合 ElRow 使用
-        </PageMain>
-      </ElCol>
-      <ElCol :md="8">
-        <PageMain style="margin: 10px 0;">
-          这里放页面内容
-        </PageMain>
-      </ElCol>
-      <ElCol :md="8">
-        <PageMain style="margin: 10px 0;">
-          这里放页面内容
-        </PageMain>
-      </ElCol>
-    </ElRow>
-
-    <PageMain title="带展开功能" collaspe height="200px">
-      <h1>Fantastic-admin</h1>
-      <img src="../../assets/images/logo.png">
-      <p>这是一款开箱即用的中后台框架,同时它也经历过数十个真实项目的技术沉淀,确保框架在开发中可落地、可使用、可维护</p>
-    </PageMain>
-  </div>
-</template>
-
-<!-- 行为区域 -->
-<!-- 样式区域 -->

+ 0 - 11
src/views/organizationManagement/organization.vue

@@ -1,11 +0,0 @@
-<template>
-  <div>
-
-  </div>
-</template>
-
-<script lang="ts">
-</script>
-
-
-

+ 353 - 0
src/views/organizationManagement/organization/organizationAdd.vue

@@ -0,0 +1,353 @@
+<template>
+  <div>
+    <PageMain style="padding: 20px;margin-bottom: 100px;">
+      <ElRow :gutter="60">
+        <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+          <span class="strong">基本信息</span>
+        </ElCol>
+        <el-form :model="sysform" label-width="120px" :rules="sysformRules" label-position="top" ref="ruleFormRef"
+          style="max-width: 1000px;" class="flex a-c f-wrap" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="编码" prop="sysCode">
+              <el-input v-model="sysform.sysCode" placeholder="请输入编码(唯一标识)" />
+              <span style="font-size: 0.7rem;color: #999;">可填写有字母数字组成的编码,用于用户登录</span>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="名称" prop="sysName">
+              <el-input v-model="sysform.sysName" placeholder="请输入名称" />
+              <span style="opacity: 0;">x</span>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="管理员姓名" prop="userName">
+              <el-input v-model="sysform.userName" placeholder="请输入管理员姓名" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="管理员账号" prop="userAccount">
+              <el-input v-model="sysform.userAccount" placeholder="请输入登录账号" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="!id">
+            <el-form-item label="登录密码" prop="password">
+              <el-input v-model="sysform.password" placeholder="请输入登录密码" show-password type="password" />
+            </el-form-item>
+          </ElCol>
+
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="简称">
+              <el-input v-model="sysform.shortName" placeholder="请输入租户简称" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="地区" prop="provinceId">
+              <el-cascader v-model="provinceoptions" style="width: 100%;" filterable clearable :options="areaOptions"
+                :props="props" @change="provinceChange" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="详细地址" prop="address">
+              <el-input v-model="sysform.address" placeholder="请输入" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="类型" prop="sysType">
+              <el-select v-model="sysform.sysType" placeholder="请选择类型" clearable>
+                <el-option v-for="(item, index) in sysTypelist" :key="index" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="租户状态" prop="available">
+              <el-select v-model="sysform.available" placeholder="请选择状态" clearable>
+                <el-option label="启用" :value="1" />
+                <el-option label="禁用" :value="0" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="品牌logo" prop="sysIcon">
+              <el-upload action="#" list-type="picture-card" :auto-upload="false" :on-change="sysIconupload"
+                ref="upload1" :limit="1" :class="{ disabled: uploadDisabled }" :on-preview="handlePictureCardPreview"
+                :on-remove="handleRemove">
+                <img v-if="sysIconList.url" :src="sysIconList.url" class="avatar" />
+                <el-icon v-else class="avatar-uploader-icon">
+                  <Plus />
+                </el-icon>
+              </el-upload>
+            </el-form-item>
+            <span style="font-size: 0.7rem;color: #999;">建议上传 120*120的照片,大小不超过5MB</span>
+
+          </ElCol>
+          <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+            <ElDivider></ElDivider>
+            <span class="strong">其他信息</span>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="联系人姓名">
+              <el-input v-model="sysform.contact" placeholder="请输入联系人姓名" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="联系电话">
+              <el-input v-model="sysform.telephone" placeholder="请输入联系电话" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="联系人地址">
+              <el-input v-model="sysform.contactAdress" placeholder="请输入联系人地址" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="联系人职务">
+              <el-input v-model="sysform.contactJob" placeholder="请输入联系人职务" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="企业规模">
+              <el-input v-model="sysform.businessSize" placeholder="输入企业规模">
+                <template #append>人</template>
+              </el-input>
+
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="法人代表">
+              <el-input v-model="sysform.legalName" placeholder="输入企业法人代表姓名" />
+            </el-form-item>
+          </ElCol>
+
+        </el-form>
+      </ElRow>
+
+    </PageMain>
+    <FixedActionBar>
+      <ElButton type="primary" size="large" @click="submitForm(ruleFormRef)">
+        保存
+      </ElButton>
+      <ElButton size="large">
+        取消
+      </ElButton>
+    </FixedActionBar>
+    <el-dialog v-model="dialogVisible">
+      <img w-full :src="dialogImageUrl" alt="Preview Image" />
+    </el-dialog>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import { FormRules, UploadFile, FormInstance, ComponentSize, ElMessage, UploadUserFile } from 'element-plus';
+import { regionData, codeToText } from "element-china-area-data";
+import { useRoute } from 'vue-router';
+import api from '@/api';
+import router from '@/router';
+const route = useRoute();
+const id = ref(route.query.id);//id参数
+const provinceoptions = ref<string[]>([])//省市区回显数据
+const areaOptions = ref(regionData)//省市区数据
+const uploadDisabled = ref(false)
+const dialogImageUrl = ref('')
+const dialogVisible = ref(false)
+const ruleFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')
+const props = {
+  expandTrigger: 'hover' as const,
+}
+const sysIconList = reactive({
+  url: "",
+})
+
+const sysform = ref({
+  id: id.value,
+  sysCode: "",
+  sysName: "",
+  shortName: "",
+  userName: "",
+  userAccount: "",
+  password: "",
+  provinceId: "",
+  cityId: "",
+  districtId: "",
+  address: "",
+  sysType: 0,
+  available: 1,
+  sysIcon: "",
+  contact: "",
+  telephone: "",
+  contactAdress: "",
+  contactJob: "",
+  businessSize: 0,//
+  legalName: "",//法人代表
+
+})
+const sysTypelist = ref([
+  {
+    id: 0,
+    name: "保险公司",
+  },
+  {
+    id: 1,
+    name: "保险分公司",
+  },
+  {
+    id: 2,
+    name: "保险经济公司",
+  },
+  {
+    id: 3,
+    name: "汽车经销商",
+  },
+  {
+    id: 4,
+    name: "保险代理公司",
+  }
+])
+const sysformRules = ref<FormRules>({
+  sysCode: [
+    { required: true, trigger: 'blur', message: '请输入编码(唯一标识)' },
+    { min: 3, max: 20, message: '请输入3~20个字符', trigger: 'blur' },
+  ],
+  sysName: [
+    { required: true, trigger: 'blur', message: '请输入名称' },
+  ],
+  shortName: [
+    { required: true, trigger: 'blur', message: '请输入简称' },
+  ],
+  userName: [
+    { required: true, trigger: 'blur', message: '请输入管理员姓名' },
+  ],
+  userAccount: [
+    { required: true, trigger: 'blur', message: '请输入管理员账号' },
+  ],
+  password: [
+    { required: true, trigger: 'blur', message: '请输入密码' },
+  ],
+
+  address: [
+    { required: true, trigger: 'blur', message: '请输入详细地址' },
+  ],
+  sysType: [
+    { required: true, trigger: 'change', message: '请选择类型' },
+  ],
+  sysIcon: [
+    { required: true, trigger: 'blur', message: '请上传图片' },
+  ],
+  provinceId: [
+    { required: true, trigger: 'change', message: '请选择省市区' },
+  ],
+  available: [
+    { required: true, trigger: 'change', message: '请选择状态' },
+  ],
+
+
+});
+onBeforeMount(() => {
+  if (id.value) {
+    detailQuery();
+  }
+});
+//详情查询
+const detailQuery = () => {
+  api.tenantApi.sysGet({ id: id.value }).then((res: any) => {
+    if (res.code == '200') {
+      sysform.value = res.data;
+      sysIconList.url = res.data.sysIconUrl
+      console.log(sysIconList.url)
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//详情编辑
+const detailUpdate = () => {
+  console.log()
+  api.tenantApi.sysEdit(sysform.value).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    } else {
+      ElMessage({
+        type: 'error',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const provinceChange = (value: any) => {
+  if (value[0] != null && value[1] != null && value[2] != null) {
+    sysform.value.provinceId = value[0];
+    sysform.value.cityId = value[1];
+    sysform.value.districtId = value[2];
+  }
+}
+const handleRemove = (file: UploadFile) => {
+  console.log(file)
+  sysIconList.url='';
+  uploadDisabled.value = false;
+
+}
+
+const handlePictureCardPreview = (file: UploadFile) => {
+  dialogImageUrl.value = file.url!
+  dialogVisible.value = true
+}
+
+
+const sysIconupload = (file: any) => {
+  let files = file.raw;
+  const params = new FormData(); // 声明formData数据类型
+  params.append("file", files);
+  api.toolApi.fileupload(params).then((res: any) => {
+    if (res.code == 200) {
+      sysform.value.sysIcon = res.data.id;
+      uploadDisabled.value = true;
+    }
+  })
+
+}
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (id.value) {
+        detailUpdate();
+      } else {
+        api.tenantApi.sysAdd(sysform.value).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+
+            })
+          }
+        })
+      }
+      router.back();
+    } else {
+
+    }
+  })
+}
+
+</script>
+<style lang="scss" scoped>
+.disabled :deep(.el-upload--picture-card) {
+  display: none;
+}
+
+// .disabled >>> .el-upload--picture-card {
+//   display: none;
+// }
+</style>

+ 452 - 0
src/views/organizationManagement/organization/organizationDetail.vue

@@ -0,0 +1,452 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <PageMain>
+      <div class="parentContainer flex  a-start" ref="mainElement">
+        <div class="flex j-start f-c a-c ContainerLeft" :style="{ height: containerLeftHeight }">
+          <div class="row-top flex f-c a-c ">
+            <img :src="tenantInfo.sysIconUrl" alt="">
+            <span class="name">{{ tenantInfo.shortName }}</span>
+            <el-tag type="primary" style="margin-bottom: 16px;" v-if="tenantInfo.available == 1">已启用</el-tag>
+            <el-tag type="danger" style="margin-bottom: 16px;" v-else>已禁用</el-tag>
+            <div class="rows">
+              <span>创建人:</span>
+              <span>{{ tenantInfo.createBy }}</span>
+            </div>
+            <div class="rows">
+              <span>管理员:</span>
+              <span>{{ tenantInfo.userName }}</span>
+            </div>
+            <div class="rows">
+              <span>创建时间:</span>
+              <span>{{ tenantInfo.createTime }}</span>
+            </div>
+            <div class="rows">
+              <span>登录账号:</span>
+              <span>{{ tenantInfo.userAccount }}</span>
+            </div>
+          </div>
+          <div class="btn flex j-s a-c">
+            <div class="item flex a-c j-c">
+              <span @click="deptAdd">添加机构</span>
+
+            </div>
+            <div class="item flex a-c j-c">
+              <el-dropdown placement="bottom-start">
+                <span class="el-dropdown-link">
+                  更 多
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item @click="del">删除</el-dropdown-item>
+                    <el-dropdown-item @click="disable">禁用</el-dropdown-item>
+                    <el-dropdown-item @click="enable">启用</el-dropdown-item>
+                    <el-dropdown-item @click="deptAdd">添加子机构</el-dropdown-item>
+
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </div>
+
+          </div>
+        </div>
+        <el-tabs v-model="activeName" class="demo-tabs">
+          <el-tab-pane label="租户信息" name="tenantInfo">
+            <el-descriptions title="基本信息" class="m-top-20" :column="3" size="large">
+              <el-descriptions-item label="编码:">{{ tenantInfo.sysCode }}</el-descriptions-item>
+              <el-descriptions-item label="名称:">{{ tenantInfo.sysName }}</el-descriptions-item>
+              <el-descriptions-item label="简称:">{{ tenantInfo.shortName }}</el-descriptions-item>
+              <el-descriptions-item label="类型:">{{ sysTypematch(tenantInfo.sysType) }}</el-descriptions-item>
+              <el-descriptions-item label="地区:">
+                {{ tenantInfo.contactAdress }}
+              </el-descriptions-item>
+              <el-descriptions-item label="详细地址:">{{ tenantInfo.address }}</el-descriptions-item>
+              <el-descriptions-item label="品牌logo:">
+                <ImagePreview :src="tenantInfo.sysIconUrl" width="50px" height="50px"></ImagePreview>
+              </el-descriptions-item>
+
+            </el-descriptions>
+            <el-divider />
+            <el-descriptions title="企业信息" class="m-top-20" :column="3" size="large">
+              <el-descriptions-item label="联系人姓名:">{{ tenantInfo.contact }}</el-descriptions-item>
+              <el-descriptions-item label="联系电话:">{{ tenantInfo.telephone }}</el-descriptions-item>
+              <el-descriptions-item label="联系人地址:">{{ tenantInfo.contactAdress }}</el-descriptions-item>
+              <el-descriptions-item label="联系人职务:">
+                {{ tenantInfo.contactJob }}
+              </el-descriptions-item>
+              <el-descriptions-item label="企业规模:">{{ tenantInfo.businessSize }}人</el-descriptions-item>
+              <el-descriptions-item label="法人代表">{{ tenantInfo.legalName }}</el-descriptions-item>
+
+            </el-descriptions>
+          </el-tab-pane>
+          <el-tab-pane label="下级机构" name="organization">
+            <TableTree :tableData="tableData" :columns="columns" :showIndex="false" :columnheight="200"
+              :showSelect="false">
+              <template v-slot:table-title-btn>
+                <RouterLink :to="{ name: 'OrganizationDeptAdd', query: { code: code } }">
+                  <el-button type="primary">
+                    添加子机构
+                    <template #icon>
+                      <el-icon>
+                        <Plus />
+                      </el-icon>
+                    </template>
+                  </el-button>
+                </RouterLink>
+              </template>
+              <template v-slot:operation="scope">
+                <el-button type="primary" link style="margin-right: 15px;"
+                  @click="addSubOrganization(scope.operationData.id)"> 添加子机构 </el-button>
+                <el-dropdown placement="bottom-start">
+                  <span class="el-dropdown-link">
+                    更 多
+                  </span>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item @click="deptUpdate(scope.operationData.id)">编辑</el-dropdown-item>
+                      <el-dropdown-item @click="deptdel(scope.operationData.id)">删除</el-dropdown-item>
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+              </template>
+            </TableTree>
+          </el-tab-pane>
+        </el-tabs>
+
+      </div>
+
+    </PageMain>
+    <el-dialog v-model="dialogVisible" title="dwd" width="40%">
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="">
+            保存
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import { ref, nextTick } from 'vue'
+import { useRoute, useRouter } from 'vue-router';
+import { TabsPaneContext, ElMessage, ElMessageBox } from 'element-plus'
+import ImagePreview from '@/components/ImagePreview/index.vue';
+
+import api from '@/api';
+const route = useRoute();
+const router = useRouter();
+const id = ref(route.query.id);//id参数
+const code = ref(route.query.code);//租户code
+const typeName = ref(route.query.typeName)
+const activeName = ref()//默认标签页
+const tableData = ref([]);//机构数据
+const mainElement = ref<HTMLElement | null>(null);
+const containerLeftHeight = ref('0px');
+const dialogVisible=ref(false)
+onMounted(() => {
+  activeName.value = typeName.value;
+  nextTick(() => {
+    if (mainElement.value) {
+      const height = mainElement.value.clientHeight;
+      containerLeftHeight.value = `${height}px`;
+    }
+  });
+});
+const exhibitionScopeformatter = (row: any, column: any, cellValue: any) => {
+  let exhibitionScope = row.exhibitionScope;
+  let sysType = exhibitionScope.join(',');
+  return () => h('div', {}, sysType);
+};
+const columns = [
+  { prop: "name", label: "机构名称", width: '300', },
+  { prop: "comType", label: "机构类型", width: '120', },
+  { prop: "exhibitionScope", label: "营业范围", width: '200', formatter: exhibitionScopeformatter },
+  { prop: "division", label: "所属地区" },
+  { prop: "leaderId", label: "负责人", width: '120', },
+
+];
+const sysTypelist = ref([
+  {
+    id: 0,
+    name: "保险公司",
+  },
+  {
+    id: 1,
+    name: "保险分公司",
+  },
+  {
+    id: 2,
+    name: "保险经济公司",
+  },
+  {
+    id: 3,
+    name: "汽车经销商",
+  },
+  {
+    id: 4,
+    name: "保险代理公司",
+  }
+])
+//类型匹配
+const sysTypematch = (value: (number | string)) => {
+  let name = "";
+  sysTypelist.value.map((val: any) => {
+    if (val.id == value) {
+      name = val.name;
+    }
+  })
+  return name;
+}
+//删除机构
+const deptdel = (id: string) => {
+  api.dept.deptDelete({ id: id }).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      getDeptTree();
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//编辑
+const deptUpdate = (id: string) => {
+  router.push({
+    name: 'OrganizationDeptAdd',
+    query: {
+      deptId: id,
+      code: code.value,
+    }
+  })
+}
+// 添加子机构
+const addSubOrganization = (id: string) => {
+  api.dept.deptGet(id).then((res: any) => {
+    if (res.code == '200') {
+      router.push({
+        name: 'OrganizationDeptAdd',
+        query: {
+          parentId: res.data.parentId,
+          code: code.value,
+        }
+      })
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+
+}
+//删除租户
+const del = () => {
+  ElMessageBox.confirm(
+    '机构删除后不可恢复,请谨慎操作',
+    '确定删除机构?',
+    {
+      confirmButtonText: '确定删除',
+      cancelButtonText: '取消',
+      type: 'error',
+    }
+  )
+    .then(() => {
+      api.tenantApi.sysDelete([id.value]).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.back();
+
+        }
+      });
+    }).catch(() => {
+
+    })
+}
+//禁用
+const disable = () => {
+  ElMessageBox.confirm(
+    '确定要禁用该业务员账号吗,禁用后该业务员账户不可登录App进行车险出单业务?',
+    '禁用租户',
+    {
+      confirmButtonText: '确定禁用',
+      cancelButtonText: '取消',
+      type: 'error',
+    }
+  )
+    .then(async () => {
+      await api.tenantApi.sysUpAndDown({ ids: [id.value], opType: 0 }).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.back();
+
+        }
+      });
+    })
+    .catch(() => {
+
+    })
+
+}
+//启用
+const enable = () => {
+  api.tenantApi.sysUpAndDown({ ids: [id.value], opType: 1 }).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+      router.back();
+    }
+  });
+}
+onBeforeMount(() => {
+  detailQuery();//获取租户详情
+  getDeptTree();//获取机构列表
+})
+interface tenantInfo {
+  [key: string]: any
+}
+const tenantInfo = ref<tenantInfo>({})
+const detailQuery = () => {
+  api.tenantApi.sysGet({ id: id.value }).then((res: any) => {
+    if (res.code == '200') {
+      tenantInfo.value = res.data;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const getDeptTree = () => {
+  api.dept.getDeptTree({ sysCode: code.value }).then((res: any) => {
+    if (res.code == '200') {
+      tableData.value = res.data[0].children;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const deptAdd = () => {
+  router.push({
+    name: 'OrganizationDeptAdd',
+    query: {
+      code: code.value
+    }
+  })
+}
+</script>
+<!-- 样式区域 -->
+<style lang='scss' scoped>
+.demo-tabs {
+  width: calc(100% - 300px);
+  margin-left: 30px;
+}
+
+:deep(.el-tabs__item) {
+  height: 60px;
+  font-size: 20px;
+  font-weight: bold !important;
+}
+
+.parentContainer {
+  display: flex;
+  width: 100%;
+
+  /* 确保容器占满可用宽度 */
+  height: calc(100vh - 200px);
+
+  .ContainerLeft {
+    border-right: 1px solid #eee;
+
+    .row-top {
+      padding: 32px 24px 24px;
+
+      >img {
+        width: 84px;
+        height: 84px;
+      }
+
+      .name {
+        margin: 16px 0 12px;
+        font-size: 20px;
+        font-weight: bold;
+      }
+
+      .rows {
+        margin: 6px 0;
+        font-size: 14px;
+        color: #666;
+
+        :last-child {
+          color: #333;
+        }
+      }
+    }
+
+    .btn {
+      width: 100%;
+
+      .item {
+        width: 50%;
+        padding: 10px;
+        font-size: 14px;
+        line-height: 1.5;
+        color: var(--el-color-primary);
+        cursor: pointer;
+        border: 1px solid #eee;
+      }
+
+      :first-child {
+        border-right: none;
+        border-left: none;
+      }
+
+      :last-child {
+        border-right: none;
+      }
+    }
+  }
+}
+
+:deep(.el-dropdown-link) {
+  display: flex;
+  align-items: center;
+  line-height: 1.5;
+  color: var(--el-color-primary);
+  cursor: pointer;
+  border-color: #fff;
+}
+
+:deep(.p-5) {
+  padding: 0;
+}
+</style>

+ 416 - 0
src/views/organizationManagement/organization/organizationlist.vue

@@ -0,0 +1,416 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <ElForm :inline="false" :model="formInline" class="demo-form-inline" label-width="100px">
+        <ElRow :gutter="20">
+          <ElCol :md="8">
+            <ElFormItem label="租户信息">
+              <el-input v-model="formInline.searchValue" placeholder="输入编码,名称查找" clearable class="el-inputs" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="8">
+            <ElFormItem label="所在地区">
+              <el-cascader v-model="provinceoptions" style="width: 100%;" filterable clearable :options="areaOptions"
+                :props="props" @change="provinceChange" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="8">
+            <ElFormItem label="类型">
+              <el-select v-model="formInline.sysType" placeholder="Activity zone" clearable class="el-inputs">
+                <el-option v-for="(item, index) in sysTypelist" :key="index" :label="item.name" :value="item.id" />
+              </el-select>
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="8">
+            <ElFormItem label="联系人信息">
+              <el-input v-model="formInline.contactValue" placeholder="输入联系人姓名,手机号查找" clearable class="el-inputs" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="6" :offset="10">
+            <ElFormItem>
+              <div class="flex j-end" style="width: 100%;">
+                <el-button type="primary" @click="roleQuery">查询</el-button>
+                <el-button type="primary" @click="resetForm">重置</el-button>
+              </div>
+            </ElFormItem>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+      <Table :tableData="tableData" :columns="columns" :showIndex="false" :columnheight="200" :showSelect="true"
+        :pageInfo="pageInfo" @getList="getList" @selectAllChange="handleSelectionChange">
+        <template v-slot:table-title-btn>
+          <RouterLink :to="{ name: 'OrganizationAdd' }">
+            <el-button type="primary">
+              添加机构
+              <template #icon>
+                <el-icon>
+                  <Plus />
+                </el-icon>
+              </template>
+            </el-button>
+          </RouterLink>
+          <el-dropdown placement="bottom-start">
+            <el-button> 批量操作<el-icon>
+                <arrow-down />
+              </el-icon>
+            </el-button>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchDel">批量删除</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchdisable">批量禁用</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchenable">批量启用</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template v-slot:operation="scope">
+          <el-button type="primary" link @click="tenantUpdate(scope.operationData)"> 编辑 </el-button>
+          <el-dropdown placement="bottom-start">
+            <span class="el-dropdown-link">
+              更 多
+            </span>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <el-dropdown-item @click="del(scope.operationData.id)">删除</el-dropdown-item>
+                <el-dropdown-item @click="disable(scope.operationData.id)">禁用</el-dropdown-item>
+                <el-dropdown-item @click="enable(scope.operationData.id)">启用</el-dropdown-item>
+                <el-dropdown-item @click="subOrganizationAdd(scope.operationData.sysCode)">添加子机构</el-dropdown-item>
+              </el-dropdown-menu>
+            </template>
+          </el-dropdown>
+        </template>
+        <template v-slot:search="scope">
+          {{ scope.slotData.label }}
+        </template>
+      </Table>
+    </PageMain>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import api from '@/api';
+import { h } from 'vue'
+import { RouterLink, useRouter } from "vue-router";
+import ImagePreview from '@/components/ImagePreview/index.vue';
+import { regionData, codeToText } from "element-china-area-data";
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+const props = {
+  expandTrigger: 'hover' as const,
+}
+const sysUpAndDownList = ref<string[]>([])//选中id
+const provinceoptions = ref<string[]>([])//省市区回显数据
+const areaOptions = ref(regionData)//省市区数据
+const provinceChange = (value: any) => {
+  if (value && value[0] != null && value[1] != null && value[2] != null) {
+    formInline.provinceId = value[0];
+    formInline.cityId = value[1];
+    formInline.districtId = value[2];
+  } else {
+    formInline.provinceId = '';
+    formInline.cityId = '';
+    formInline.districtId = '';
+  }
+}
+const router = useRouter();
+const pageInfo = ref({
+  pageNum: 1,
+  pageSize: 20,
+  sizes: [10, 20, 30, 40, 50],
+  total: 100,
+});
+const sysTypelist = ref([
+  {
+    id: 0,
+    name: "保险公司",
+  },
+  {
+    id: 1,
+    name: "保险分公司",
+  },
+  {
+    id: 2,
+    name: "保险经济公司",
+  },
+  {
+    id: 3,
+    name: "汽车经销商",
+  },
+  {
+    id: 4,
+    name: "保险代理公司",
+  }
+])
+const formInline = reactive({
+  searchValue: '',
+  contactValue: '',
+  sysType: 0,
+  provinceId: "",
+  cityId: "",
+  districtId: "",
+})
+const tableData = ref([]);
+onBeforeMount(() => {
+  findpage();
+});
+function findpage() {
+  api.tenantApi.sysList({ pageNum: pageInfo.value.pageNum, pageSize: pageInfo.value.pageSize, ...formInline }).then((res: any) => {
+    if (res.code == 200) {
+      tableData.value = res.data.records;
+      pageInfo.value.pageNum = res.data.page;
+      pageInfo.value.pageSize = res.data.size;
+      pageInfo.value.total = res.data.total;
+    }
+
+  })
+}
+const deptCountformatter = (row: any, column: any, cellValue: any) => {
+  return () => h('div', {}, [
+    row.id
+      ? h(RouterLink, {
+        to: { name: 'OrganizationDetail', query: { id: row.id, code: row.sysCode,typeName:'organization'} },
+        style: { color: '#3498db', textDecoration: 'none' },
+      }, { default: () => row.deptCount || 'N/A' })
+      : h('span', { style: { color: 'gray' } }, 'Loading...') // 加载状态或占位符
+  ]);
+};
+const sysNameformatter = (row: any, column: any, cellValue: any) => {
+  return () => h('div', {}, [
+    row.id
+      ? h(RouterLink, {
+        to: { name: 'OrganizationDetail', query: { id: row.id, code: row.sysCode,typeName:'tenantInfo' } },
+        style: { color: '#3498db', textDecoration: 'none' },
+      }, { default: () => row.sysName || 'N/A' })
+      : h('span', { style: { color: 'gray' } }, 'Loading...') // 加载状态或占位符
+  ]);
+};
+const imgformatter = (row: any, column: any, cellValue: any) => {
+  // 返回虚拟 DOM,使用 `<ImagePreview>` 组件
+  return h('div', {}, [
+    h(ImagePreview, {
+      src: row.sysIconUrl, // 显示图片的路径(替换为你的实际数据属性)
+      width: '50px',
+      height: '50px',
+      style: {
+        cursor: 'pointer',
+      },
+    }),
+  ]);
+};
+const typeformatter = (row: any, column: any, cellValue: any) => {
+  let sysType = '';
+  sysTypelist.value.map(ele => {
+    if (ele.id == row.sysType) {
+      sysType = ele.name;
+    }
+  })
+
+  return () => h('div', {}, sysType);
+};
+const cityformatter = (row: any, column: any, cellValue: any) => {
+  return () => h('div', {}, `${codeToText[row.provinceId]}-${codeToText[row.cityId]}-${codeToText[row.districtId]}`);
+};
+const statusformatter = (row: any, column: any, cellValue: any) => {
+  let available = row.available;
+  if (available) {
+    return () => h('div', {}, '启用');
+  } else {
+    return () => h('div', {}, '禁用');
+  }
+};
+const columns = [
+  { prop: "sysCode", label: "编码", width: '120', },
+  { prop: "sysName", label: "租户名称", width: '120', formatter: sysNameformatter },
+  { prop: "sysIcon", label: "租户图标", width: '120', formatter: imgformatter },
+  { prop: "sysType", label: "租户类型", width: '120', formatter: typeformatter },
+  { prop: "provinceId", label: "所在地区", formatter: cityformatter },
+  { prop: "deptCount", label: "下级机构", formatter: deptCountformatter },
+  { prop: "available", label: "状态", width: '120', formatter: statusformatter },
+  { prop: "createTime", label: "创建时间", width: '180', },
+  { prop: "createBy", label: "创建人", width: '120', },
+];
+
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+};
+function tenantUpdate(item: any) {
+  router.push({
+    name: 'OrganizationAdd',
+    query: {
+      id: item.id
+    }
+  })
+}
+const handleSelectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+//删除
+const del = (value: string) => {
+  ElMessageBox.confirm(
+    '机构删除后不可恢复,请谨慎操作',
+    '确定删除机构?',
+    {
+      confirmButtonText: '确定删除',
+      cancelButtonText: '取消',
+      type: 'error',
+    }
+  )
+    .then(() => {
+      api.tenantApi.sysDelete([value]).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          findpage();
+        }
+      });
+    }).catch(() => {
+
+    })
+
+
+
+}
+//禁用
+const disable = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要禁用该业务员账号吗,禁用后该业务员账户不可登录App进行车险出单业务?',
+    '禁用租户',
+    {
+      confirmButtonText: '确定禁用',
+      cancelButtonText: '取消',
+      type: 'error',
+    }
+  )
+    .then(() => {
+      sysUpAndDown(0, [value]);
+    })
+    .catch(() => {
+
+    })
+
+}
+//启用
+const enable = (value: string) => {
+  sysUpAndDown(1, [value]);
+}
+//批量删除
+const batchDel = () => {
+  ElMessageBox.confirm(
+    '机构删除后不可恢复,请谨慎操作',
+    '确定删除机构?',
+    {
+      confirmButtonText: '确定删除',
+      cancelButtonText: '取消',
+      type: 'error',
+    }
+  )
+    .then(() => {
+      api.tenantApi.sysDelete(sysUpAndDownList.value).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          findpage();
+
+        }
+      });
+    })
+    .catch(() => {
+
+    })
+
+}
+//批量禁用
+const batchdisable = () => {
+  ElMessageBox.confirm(
+    '确定要禁用该业务员账号吗,禁用后该业务员账户不可登录App进行车险出单业务?',
+    '禁用租户',
+    {
+      confirmButtonText: '确定禁用',
+      cancelButtonText: '取消',
+      type: 'error',
+    }
+  )
+    .then(async () => {
+      await sysUpAndDown(0, undefined);
+    })
+    .catch(() => {
+    })
+
+
+}
+//批量启用
+const batchenable = async () => {
+  await sysUpAndDown(1, undefined);
+}
+const roleQuery = () => {
+  findpage();
+}
+const sysUpAndDown = async (type: number, list?: string[]) => {
+  await api.tenantApi.sysUpAndDown({ ids: list ? list : sysUpAndDownList.value, opType: type }).then((res: any) => {
+    if (res.code == '200') {
+      findpage();
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//添加子机构
+const subOrganizationAdd = (code: string) => {
+  router.push({
+    name: 'OrganizationDeptAdd',
+    query: {
+      code: code
+    }
+  })
+}
+const resetForm = () => {
+  formInline.searchValue = '';
+  formInline.contactValue = '';
+  formInline.provinceId = '';
+  formInline.cityId = '';
+  formInline.districtId = '';
+  formInline.sysType = 0;
+
+}
+
+</script>
+
+<style lang="scss" scoped>
+.demo-form-inline .el-input {
+  --el-input-width: 220px;
+}
+
+.demo-form-inline .el-select {
+  --el-select-width: 220px;
+}
+
+:deep(.el-dropdown-link) {
+  display: flex;
+  align-items: center;
+  margin-left: 15px;
+  color: var(--el-color-primary);
+  cursor: pointer;
+}
+
+.el-inputs {
+  width: 100% !important;
+}
+</style>

+ 298 - 0
src/views/organizationManagement/organization/subOrganizatioAdd.vue

@@ -0,0 +1,298 @@
+<template>
+  <div>
+    <PageMain style="padding: 20px;margin-bottom: 100px;">
+      <ElRow :gutter="60">
+        <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+          <span class="strong">基本信息</span>
+        </ElCol>
+        <el-form :model="deptform" label-width="120px" :rules="deptformRules" label-position="top" ref="deptFormRef"
+          style="max-width: 1000px;" class="flex a-c f-wrap" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="机构名称" prop="name">
+              <el-input v-model="deptform.name" placeholder="请输入50个字以内的机构名称" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="上级机构" prop="parentId" >
+              <el-cascader v-model="parentIdareaOptionsValue" :disabled="parentIdShow" placeholder="请选择地区" style="width: 100%;" filterable
+                clearable :options="parentIdareaOptions" :props="parentIdprops" @change="parentIdChange"
+                :show-all-levels="false" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="机构类型" prop="comType">
+              <el-select v-model="deptform.comType" placeholder="请选择类型" clearable>
+                <el-option v-for="(item, index) in sysTypelist" :key="index" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="地区" prop="province">
+              <el-cascader v-model="provinceoptionsValue" placeholder="请选择地区" style="width: 100%;" filterable clearable
+                :options="areaOptions" :props="props" @change="provinceChange" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="展业范围" prop="exhibitionScope">
+              <el-select v-model="deptform.exhibitionScope" multiple placeholder="请选择展业范围" clearable>
+                <el-option :label="item.label" v-for="(item, index) in dictLists.exhibition_scopeList" :key="index"
+                  :value="item.label" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="机构地址" prop="address">
+              <el-input v-model="deptform.address" placeholder="请输入机构地址" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="机构负责人" prop="leaderId">
+              <el-input v-model="deptform.leaderId" placeholder="请输入机构负责人" />
+
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+
+    </PageMain>
+    <FixedActionBar>
+      <ElButton type="primary" size="large" @click="submitForm(deptFormRef)">
+        保存
+      </ElButton>
+      <ElButton size="large">
+        取消
+      </ElButton>
+    </FixedActionBar>
+    <el-dialog v-model="dialogVisible">
+      <img w-full :src="dialogImageUrl" alt="Preview Image" />
+    </el-dialog>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { FormRules, FormInstance, ComponentSize, ElMessage } from 'element-plus';
+// import { regionData } from "element-china-area-data";
+import { useRoute } from 'vue-router';
+import api from '@/api';
+import router from '@/router';
+const route = useRoute();
+const code = ref(route.query.code);//租户id参数
+const deptId = ref(route.query.deptId)//编辑数据id标识
+const parentId = ref(route.query.parentId)//上级id标识
+const provinceoptionsValue = ref()//省市区回显数据
+const areaOptions = ref()//省市区数据
+const parentIdShow=ref(false);//上级机构是否可修改
+const parentIdareaOptions = ref();//上级机构数据
+const parentIdareaOptionsValue = ref();//上级机构回显数据
+const dialogImageUrl = ref('')
+const dictLists = ref<{ [key: string]: any[] }>({});
+const dialogVisible = ref(false)
+const deptFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')
+const props = {
+  expandTrigger: 'hover' as const,
+  value: 'areaCode',
+  label: 'areaCname',
+  children: 'child'
+}
+const parentIdprops = {
+  expandTrigger: 'hover' as const,
+  value: 'id',
+  label: 'name',
+  checkStrictly: true,
+}
+const deptform = ref({
+  name: "",//机构名称
+  parentId: "",//上级机构
+  comType: "",//机构类型
+  exhibitionScope: [],//展业范围
+  province: "",//省
+  city: "",//市
+  area: "",//区
+  address: "",//机构地址
+  leaderId: "32131",//负责人id
+  systemCode: code.value,//租户名称
+  id: deptId.value,
+})
+const sysTypelist = ref([
+  {
+    id: "0",
+    name: "保险公司",
+  },
+  {
+    id: "1",
+    name: "保险分公司",
+  },
+  {
+    id: "2",
+    name: "保险经济公司",
+  },
+  {
+    id: "3",
+    name: "汽车经销商",
+  },
+  {
+    id: "4",
+    name: "保险代理公司",
+  }
+])
+const deptformRules = ref<FormRules>({
+  name: [
+    { required: true, trigger: 'blur', message: '请输入50个字以内的机构名称' },
+    { max: 50, message: '请输入0~50个字符', trigger: 'blur' },
+  ],
+  parentId: [
+    { required: true, trigger: 'change', message: '请选择上级机构' },
+  ],
+  comType: [
+    { required: true, trigger: 'blur', message: '请输入简称' },
+  ],
+  exhibitionScope: [
+    { required: true, trigger: 'change', message: '请选择展业范围' },
+  ],
+  province: [
+    { required: true, trigger: 'change', message: '请选择地区' },
+  ],
+  address: [
+    { required: true, trigger: 'blur', message: '请输入机构地址' },
+  ],
+});
+onBeforeMount(() => {
+  dictType('exhibition_scope');//展业范围
+  getDeptTree();//获取上级
+  queryTreeQuery();//获取区划数据
+  deptInfo();//获取机构信息详情
+
+  if(parentId.value){
+    parentIdareaOptionsValue.value=parentId.value;
+    //子机构向下添加,上级机构无法修改
+
+    parentIdShow.value=true;
+  }
+});
+//获取上级机构
+const getDeptTree = () => {
+  api.dept.getDeptTree({ sysCode: code.value }).then((res: any) => {
+    if (res.code == '200') {
+      parentIdareaOptions.value = res.data;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//获取区划数据
+const queryTreeQuery = () => {
+  api.dept.queryTree().then((res: any) => {
+    if (res.code == '200') {
+      areaOptions.value = res.data;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//获取机构详细信息
+const deptInfo = () => {
+  if (deptId.value && code.value) {
+    api.dept.deptGet(deptId.value).then((res: any) => {
+      if (res.code == '200') {
+        deptform.value.name = res.data.name;
+        deptform.value.parentId = res.data.parentId;
+        deptform.value.comType = res.data.comType;
+        parentIdareaOptionsValue.value = res.data.parentId;
+        provinceoptionsValue.value = [res.data.province, res.data.city, res.data.area]
+        deptform.value.exhibitionScope = res.data.exhibitionScope;
+        deptform.value.address = res.data.address;
+        deptform.value.leaderId = res.data.leaderId;
+        deptform.value.province = res.data.province;
+        deptform.value.city = res.data.city;
+        deptform.value.area = res.data.area;
+
+      } else {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+      }
+    });
+  }
+
+}
+//字典信息处理
+const dictType = (type: string) => {
+  api.toolApi.dictType({ dictCode: type }).then((res: any) => {
+    if (res.code == '200') {
+      dictLists.value[type + 'List'] = res.data.map((item: any) => {
+        return {
+          value: item.value,
+          label: item.label
+        };
+      });
+    }
+  });
+}
+//省市区回调
+const provinceChange = (value: any) => {
+  console.log(value)
+  if (value && value[0] != null && value[1] != null && value[2] != null) {
+    deptform.value.province = value[0];
+    deptform.value.city = value[1];
+    deptform.value.area = value[2];
+  } else {
+    deptform.value.province = "";
+    deptform.value.city = "";
+    deptform.value.area = "";
+  }
+}
+//上级机构回调
+const parentIdChange = (value: any) => {
+  deptform.value.parentId = value[value.length - 1];
+}
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (deptId.value) {
+        api.dept.deptUpdate(deptform.value).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back();
+          }
+        })
+      }else{
+        api.dept.deptAdd(deptform.value).then((res: any) => {
+        if (res.code == 200) {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          router.back();
+        }
+      })
+      }
+    } else {
+      ElMessage({
+            type: 'error',
+            message: '信息不完整',
+            plain: true,
+          })
+    }
+  })
+}
+
+</script>

+ 221 - 0
src/views/organizationManagement/tenantPermissions.vue

@@ -0,0 +1,221 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <ElRow :gutter="20" style="height: 100%;">
+        <ElCol :md="5">
+          <div class="flex f-c " style="height: 100%;">
+            <ElFormItem>
+              <ElInput v-model="searchValue" placeholder="输入编码,名称查找" type="text" tabindex="1" style="width: 100%;">
+                <template #prefix>
+                  <SvgIcon name="i-ri:user-3-fill" />
+                </template>
+                <template #append>
+                  <el-button :icon="Search" class="ElInputSearch" @click="findpage" />
+                </template>
+              </ElInput>
+            </ElFormItem>
+            <div style=" width: 100%;height: calc(100vh - 350px);  overflow-y: auto;" class="container">
+              <div class="pad-1 roleitem " :class="{ active: roleId === item.id }" v-for="(item, index) in tenantList"
+                :key="index" @click="roleitemClick(item)">
+                <span>{{ item.sysName }}</span>
+              </div>
+            </div>
+          </div>
+        </ElCol>
+        <ElCol :md="1">
+          <ElDivider direction="vertical" style="height: 100%;"></ElDivider>
+        </ElCol>
+        <ElCol :md="18">
+          <div class="flex j-s a-c">
+            <span class="strong">操作权限</span>
+            <div>
+              <ElButton type="default" @click="roleSubmit">保存</ElButton>
+            </div>
+          </div>
+          <ElDivider style="margin-top: 10px;"></ElDivider>
+          <ElTree style="max-height: 600px;overflow-y: auto;" ref="treeRef" :data="treeData" :props="defaultProps"
+            show-checkbox node-key="id" :default-checked-keys="checkedKeys" @check="handleCheckChange"
+            @check-change="treecheckChange" />
+
+        </ElCol>
+      </ElRow>
+
+    </PageMain>
+    <!-- <template #title>
+        <div class="flex j-s a-c">
+          <span class="strong">角色管理</span>
+          <div>
+            <ElButton type="primary" @click="roleSubmit">提交</ElButton>
+            <ElButton v-if="roleInfo.id" type="danger" @click="roleDelClick">删除</ElButton>
+          </div>
+        </div>
+      </template> -->
+  </div>
+</template>
+
+<script lang='ts' setup>
+import { ref, reactive, onBeforeMount } from 'vue';
+import { ElTree, ElMessage } from 'element-plus';
+import { TreeNode } from '@/utils/composables/routeUtils'; // 导入工具方法
+import { Search } from '@element-plus/icons-vue'
+import api from '@/api'
+import Message from 'vue-m-message'
+
+onBeforeMount(() => {
+  findpage()//租户列表
+  getMenu();//获取租户菜单
+});
+interface tenantList {
+  sysName: string,
+  id: string,
+  [key: string]: any
+}
+const treeRef = ref<InstanceType<typeof ElTree>>();
+const roleId = ref<string>("");
+const PageNum = ref<number>(1);
+const PageSize = ref<number>(1000);
+const checkedKeys = ref<string[]>([])
+
+const identifier = ref(false)
+const roleInfo = reactive<{
+  systemCode: string;
+  permsId: (number | string)[],
+  perms: object[],
+  systemConfig: object[],
+}>({
+  systemCode: "",//角色
+  systemConfig: [],//菜单json
+  permsId: [],//权限id
+  perms: [],//权限标识
+});
+const searchValue = ref<string>()
+const tenantList = ref<tenantList>()
+const treeData = ref<TreeNode[]>([]);//菜单数据
+const selectedData = ref<TreeNode[]>([]);
+const defaultProps = {
+  children: 'children',
+  label: 'title',
+};
+/**
+ *
+ * @param checkedNodes 选中的节点数据
+ * @param show 选中状态
+ */
+const treecheckChange = (checkedNodes: any, show: boolean, item: any) => {
+  if (show) {
+    selectedData.value.push(checkedNodes)
+  } else {
+    let updatedNodes = selectedData.value.filter((node: any) => node.id !== checkedNodes.id);
+    selectedData.value = updatedNodes;
+  }
+}
+/**
+ *
+ * @param _checkedNodes 选中的节点id集合
+ * @param checkedInfo 选中的节点对象
+ */
+const handleCheckChange = (
+  _checkedNodes: TreeNode[],
+  checkedInfo: {
+    checkedKeys: number[];
+    checkedNodes: TreeNode[];
+    halfCheckedKeys: number[];
+    halfCheckedNodes: TreeNode[];
+  }
+) => {
+  roleInfo.permsId = checkedInfo.checkedKeys;
+  let filteredNodes = checkedInfo.checkedNodes.filter(node => node.type === '2');
+  roleInfo.perms = [];
+  roleInfo.perms = filteredNodes;
+};
+const getMenu = () => {
+  api.tenantApi.getSystemConfig().then((res: any) => {
+    if (res.code == '200') {
+      treeData.value = JSON.parse(res.data.sysConfig)
+      console.log(treeData.value)
+    }
+  });
+}
+async function roleSubmit() {
+  api.tenantApi.updatePerms(roleInfo).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    } else {
+    }
+  });
+
+}
+
+async function roleitemClick(item: { sysCode: string }) {
+  roleInfo.systemCode = item.sysCode;
+}
+const findpage = () => {
+  api.tenantApi.sysList({ pageNum: PageNum.value, pageSize: PageSize.value, searchValue: searchValue.value }).then((res: any) => {
+    if (res.code == 200) {
+      tenantList.value = res.data.records;
+    }
+  })
+}
+function resetForm() {
+  identifier.value = true;
+  roleInfo.permsId = [];
+  roleInfo.perms = [];
+  roleInfo.systemConfig = [];
+  if (treeRef.value) {
+    treeRef.value.setCheckedKeys([]);
+  }
+}
+
+
+</script>
+
+<style lang="scss" scoped>
+.roleitem {
+  span:first-child {
+    font-size: 16px;
+    color: #333;
+  }
+
+  span:last-child {
+    font-size: 14px;
+    color: #666;
+  }
+}
+
+.roleitem:hover {
+  background: #f4faff;
+  transition: 0.2s;
+}
+
+.active {
+  background: #f4faff;
+  border-right: 2px solid #0083ff;
+}
+
+.el-pagination {
+  position: absolute;
+  bottom: 0;
+}
+
+.ElInputSearch {
+  color: #fff !important;
+  background: linear-gradient(91deg, #4fcaff 0%, #2b62ff 100%) !important;
+  border-color: #4fcaffe8 !important;
+  border-top-left-radius: 0 !important;
+  border-bottom-left-radius: 0 !important;
+}
+
+.footer {
+  width: 100%;
+}
+
+.container::-webkit-scrollbar {
+  display: none;
+
+  /* 针对 Webkit 浏览器 (如 Chrome/Safari) */
+}
+</style>

+ 85 - 0
src/views/personnel/memberAdd.vue

@@ -0,0 +1,85 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <PageMain style="padding: 20px;margin-bottom: 100px;">
+      <ElRow :gutter="60">
+        <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+          <span class="strong">基本信息</span>
+        </ElCol>
+        <el-form :model="memberform" label-width="120px" :rules="memberformRules" label-position="top" ref="ruleFormRef"
+          style="max-width: 1000px;" class="flex a-c f-wrap" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="姓名" prop="sysCode">
+              <el-input v-model="memberform.username" placeholder="请输入编码(唯一标识)" />
+              <span style="font-size: 0.7rem;color: #999;">可填写有字母数字组成的编码,用于用户登录</span>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="名称" prop="sysName">
+              <el-input v-model="memberform.username" placeholder="请输入名称" />
+              <span style="opacity: 0;">x</span>
+            </el-form-item>
+          </ElCol>
+
+
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="简称">
+              <el-input v-model="memberform.username" placeholder="请输入租户简称" />
+            </el-form-item>
+          </ElCol>
+
+
+
+        </el-form>
+      </ElRow>
+
+    </PageMain>
+    <FixedActionBar>
+      <ElButton type="primary" size="large" >
+        保存
+      </ElButton>
+      <ElButton size="large">
+        取消
+      </ElButton>
+    </FixedActionBar>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import { ref, reactive } from 'vue'
+import api from '@/api'
+import { FormRules, FormInstance, ComponentSize, ElMessage } from 'element-plus';
+const roleList = ref([])
+const memberformRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')
+const memberformRules = ref<FormRules>({
+  username: [
+    { required: true, trigger: 'blur', message: '请输入成员姓名' },
+    { min: 3, max: 20, message: '请输入0~20个字符', trigger: 'blur' },
+  ],
+  mobile: [
+    { required: true, trigger: 'blur', message: '请输入手机号' },
+  ],
+  roleVoList: [
+    { required: true, trigger: 'change', message: '请选择角色' },
+  ],
+});
+const memberform = ref({
+  username: "",
+  mobile:"",
+  roleVoList: [],
+})
+onBeforeMount(() => {
+  roleListfun();
+})
+const roleListfun = () => {
+  api.roleApi.roleList({ pageNum: 1, pageSize: 20, name: "" }).then((res: any) => {
+    if (res.code == 200) {
+      roleList.value = res.data;
+
+    }
+  })
+}
+</script>
+<!-- 样式区域 -->

+ 422 - 252
src/views/personnel/memberManagement.vue

@@ -1,86 +1,71 @@
 <template>
-  <div class="member">
-    <el-form label-position="left" label-width="auto">
-      <el-row :gutter="16">
-        <el-col :span="6">
-          <el-form-item label="成员信息">
-            <el-input
-              v-model="formAccessibility.firstName"
-              placeholder="输入姓名,手机号,工号查询"
-            />
-          </el-form-item>
-        </el-col>
-        <el-col :span="6">
-          <el-form-item label="状态">
-            <el-select
-              v-model="value"
-              placeholder="成员状态"
-              style="width: 240px"
-            >
-              <el-option
-                v-for="item in options"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
-              />
-            </el-select>
-          </el-form-item>
-        </el-col>
-        <el-col :span="4">
-          <el-form-item label="">
-            <el-button type="primary">查询</el-button>
-            <el-button type="primary" plain @click="reset">重置</el-button>
-          </el-form-item>
-        </el-col>
-        <!-- <el-col :span="24">
-          <el-form-item label="">
-            <el-button type="primary" @click="add(ruleFormRef)">
-              <el-icon><Plus /></el-icon>添加成员</el-button
-            >
-         
-          </el-form-item>
-        </el-col> -->
-      </el-row>
-    </el-form>
-   
-      <Table
-        :tableData="tableData"
-        :columns="columns"
-        :showIndex="false"
-        :showSelect="true"
-        :pageInfo="pageInfo"
-        @getList="getList"
-      >
+  <div>
+    <PageMain class="vh100">
+      <ElForm :inline="false" :model="formInline" class="demo-form-inline" label-width="100px">
+        <ElRow :gutter="20">
+          <ElCol :md="8">
+            <ElFormItem label="成员信息">
+              <el-input v-model="formInline.searchValue" placeholder="输入姓名,手机号,工号查找" clearable class="el-inputs" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="8">
+            <ElFormItem label="状态">
+              <el-select v-model="formInline.isEnable" placeholder="选择状态" clearable class="el-inputs">
+                <el-option label="全部" value=" " />
+                <el-option label="启用" :value="1" />
+                <el-option label="禁用" :value="0" />
+              </el-select>
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="8">
+            <ElFormItem>
+              <div class="flex j-end" style="width: 100%;">
+                <el-button type="primary" @click="memberQuery" @keydown.enter="handleEnter">查询</el-button>
+                <el-button type="default" @click="resetForm">重置</el-button>
+              </div>
+            </ElFormItem>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+      <ElDivider border-style="dashed"></ElDivider>
+      <Table :tableData="tableData" :columns="columns" :showIndex="false" :columnheight="200" :showSelect="true"
+        :pageInfo="pageInfo" @getList="getList" @selectAllChange="handleSelectionChange" @selectChange="selectChange">
         <template v-slot:table-title-btn>
-          <el-button type="primary" @click="add(ruleFormRef)">
-              <el-icon><Plus /></el-icon>添加成员</el-button
-            >
+          <el-button type="primary" @click="userAdd">
+            添加成员
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
           <el-dropdown placement="bottom-start">
-            <el-button> 批量操作<el-icon class="el-icon--right">
+            <el-button> 批量管理<el-icon>
                 <arrow-down />
               </el-icon>
             </el-button>
             <template #dropdown>
               <el-dropdown-menu>
-                <el-dropdown-item>批量删除</el-dropdown-item>
-                <el-dropdown-item>批量禁用</el-dropdown-item>
-                <el-dropdown-item>批量启用</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchDel">批量删除</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchdisable">批量禁用</el-dropdown-item>
+                <el-dropdown-item :disabled="!sysUpAndDownList.length" @click="batchenable">批量启用</el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
         </template>
         <template v-slot:operation="scope">
-          <el-button type="primary" link @click="editor(scope)"> 编辑 </el-button>
+          <el-button type="primary" link @click="tenantUpdate(scope.operationData.id)"> 编辑 </el-button>
           <el-dropdown placement="bottom-start">
             <span class="el-dropdown-link">
               更 多
-            
             </span>
             <template #dropdown>
               <el-dropdown-menu>
-                <el-dropdown-item>删除</el-dropdown-item>
-                <el-dropdown-item>禁用</el-dropdown-item>
-                <el-dropdown-item>启用</el-dropdown-item>
+                <el-dropdown-item @click="del(scope.operationData.id)">删除</el-dropdown-item>
+                <el-dropdown-item v-if="scope.operationData.accountStatus == 1"
+                  @click="disable(scope.operationData.id)">禁用</el-dropdown-item>
+                <el-dropdown-item v-if="scope.operationData.accountStatus == 0"
+                  @click="enable(scope.operationData.id)">启用</el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
@@ -89,231 +74,416 @@
           {{ scope.slotData.label }}
         </template>
       </Table>
-   
-    <el-dialog v-model="dialogVisible" title="基本信息" width="800" draggable>
-      <el-form
-        label-position="top"
-        label-width="auto"
-        :rules="rules"
-        :model="form"
-        ref="ruleFormRef"
-      >
-        <el-row :gutter="24">
-          <el-col :span="12">
-            <el-form-item label="姓名" prop="name">
-              <el-input
-                v-model.trim="form.name"
-                maxlength="20"
-                placeholder="输入人员姓名"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="手机号" prop="phone">
-              <el-input
-                v-model.number.trim="form.phone"
-                maxlength="11"
-                placeholder="输入人员手机号"
-              />
+    </PageMain>
+
+    <el-dialog v-model="UpdatedialogVisible" :title="dialogTitle" width="40%">
+      <ElRow :gutter="20">
+        <el-form :model="memberform" :inline="true" label-width="120px" :rules="memberformRules" label-position="top"
+          ref="memberFormRef" style="width: 100%;" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="姓名" prop="username">
+              <el-input v-model="memberform.username" placeholder="请输入姓名" />
             </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="登录密码" prop="password">
-              <el-input
-                v-model.trim="form.password"
-                maxlength="20"
-                placeholder="输入登录密码(8-20位,包含字母和数字)"
-              />
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="手机号" prop="mobile">
+              <el-input v-model="memberform.mobile" placeholder="请输入手机号" show-word-limit maxlength="11" />
             </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="角色" prop="role">
-              <el-select
-                v-model="form.role"
-                placeholder="选择角色"
-                style="width: 240px"
-              >
-                <el-option
-                  v-for="item in options"
-                  :key="item.value"
-                  :label="item.label"
-                  :value="item.value"
-                />
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="角色" prop="roleNameList">
+              <el-select v-model="memberform.roleNameList" multiple placeholder="请选择角色" style="width: 100%;">
+                <el-option :label="item.name" :value="item.id" v-for="(item, index) in roleLists" :key="index" />
               </el-select>
             </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
+          </ElCol>
+        </el-form>
+      </ElRow>
+
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="dialogVisible = false">取 消</el-button>
-          <el-button type="primary" @click="dialogVisible = false">
-            保 
+          <el-button @click="UpdatedialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="memberAdd">
+            保存
           </el-button>
         </div>
       </template>
     </el-dialog>
   </div>
+
 </template>
 
-<script setup lang="ts">
-import { ref, reactive } from "vue";
-import type { ComponentSize, FormInstance, FormRules } from "element-plus";
-import { Plus,ArrowDown } from "@element-plus/icons-vue";
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import api from '@/api';
+import { h } from 'vue'
+import { RouterLink, useRouter } from "vue-router";
+// import ImagePreview from '@/components/ImagePreview/index.vue';
+import { ElMessage, ElTag, ElMessageBox } from 'element-plus'
+import { FormRules, FormInstance, ComponentSize } from 'element-plus';
 
-// import universalTable from "../"
-const formAccessibility = reactive({
-  fullName: "",
-  firstName: "",
-  lastName: "",
-  phone: "",
-});
-const ruleFormRef = ref<FormInstance>();
-const form = reactive({
-  name: "",
-  role: "",
-  password: "",
-  phone: "",
-});
-
-interface RuleForm {
-  name: string;
-  phone: string;
-  role: string[];
-  password: string;
-}
 interface PageInfo {
   pageNum: number;
   pageSize: number;
   sizes: number[];
   total: number;
 }
-const value = ref("");
-const dialogVisible = ref(false);
-const options = [
-  {
-    value: "Option1",
-    label: "Option1",
-  },
-  {
-    value: "Option2",
-    label: "Option2",
-  },
-  {
-    value: "Option3",
-    label: "Option3",
-  },
-  {
-    value: "Option4",
-    label: "Option4",
-  },
-  {
-    value: "Option5",
-    label: "Option5",
-  },
-];
-const validatePhone = (rule: any, value: string, callback: any) => {
-  const reg = /^1[3-9]\d{9}$/;
-  if (!value) {
-    return callback(new Error("手机号码不能为空"));
-  }
-  if (!reg.test(value)) {
-    return callback(new Error("请输入正确的手机号码"));
-  }
-  return callback();
-};
-const validatePassword = (rule: any, value: string, callback: any) => {
-  const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,20}$/;
-  if (!value) {
-    return callback(new Error("密码不能为空"));
-  }
-  if (!reg.test(value)) {
-    return callback(new Error("请输入正确的密码格式"));
-  }
-  return callback();
-};
-const rules = reactive<FormRules<RuleForm>>({
-  name: [
-    { required: true, message: "请输入成员姓名", trigger: "blur" },
-    { min: 1, max: 20, message: "请输入成员姓名", trigger: "blur" },
+interface roleLists {
+  id: string,
+  name: string,
+  [key: string]: any
+}
+const tableData = ref([]);//列表数据
+const memberFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')//表单大小
+const UpdatedialogVisible = ref(false)//弹窗
+const sysUpAndDownList = ref<string[]>([])//选中id
+const roleLists = ref<roleLists[]>([])//角色列表
+const dialogTitle = ref('添加成员')//弹窗title
+const dialogType = ref('add')//弹窗title
+const router = useRouter();
+const memberform = reactive({
+  username: "",//机构名称
+  mobile: "",//上级机构
+  roleNameList: [],
+  id: '',
+})
+const memberformRules = ref<FormRules>({
+  username: [
+    { required: true, trigger: 'blur', message: '请输入姓名' },
+    { min: 2, max: 20, message: '请输入2~20个字符', trigger: 'blur' },
+  ],
+  roleNameList: [
+    { required: true, trigger: 'change', message: '请选择角色' },
+  ],
+  mobile: [
+    { required: true, trigger: 'blur', message: '请输入手机号' },
   ],
-  phone: [{ required: true, validator: validatePhone, trigger: "blur" }],
-  password: [{ required: true, validator: validatePassword, trigger: "blur" }],
-  role: [{ required: true, message: "请选择角色", trigger: "change" }],
-});
-// import Select from "@/components/select/selectMain.vue";
-// import TestTable from "@/components/Table/index.vue";
-const tableData = [
-  {
-    size: "2024/12/1",
-    Name: "Dec",
-    Phone: "13333333333",
-    role: "公司",
-    status: "123",
-  },
-];
-const columns = [
-  { prop: "Name", label: "姓名" },
-  { prop: "Phone", label: "手机号" },
-  { prop: "size", label: "工号" },
-  { prop: "role", label: "角色" },
-  { prop: "status", label: "状态" },
 
-  // {
-  //     prop: "operation", label: "operation", width: "280",
-  //     type:'operation', btn:[{
-  //         type:"pramary",
-  //         label:"编辑"
-  //     }]
-  // },
-];
-const pageInfo = {
+});
+//分页
+const pageInfo = ref({
   pageNum: 1,
-  pageSize: 10,
+  pageSize: 20,
   sizes: [10, 20, 30, 40, 50],
   total: 100,
+});
+//检索条件
+const formInline = reactive({
+  searchValue: '',
+  isEnable: '',
+})
+onMounted(() => {
+  findpage();
+  roleListfun();
+  window.addEventListener('keydown', handleEnter)
+});
+onUnmounted(() => {
+  window.removeEventListener('keydown', handleEnter, false)
+})
+function findpage() {
+  api.member.userList({ page: pageInfo.value.pageNum, size: pageInfo.value.pageSize, ...formInline }).then((res: any) => {
+    if (res.code == 200) {
+      tableData.value = res.data.records;
+      pageInfo.value.pageNum = res.data.current;
+      pageInfo.value.pageSize = res.data.size;
+      pageInfo.value.total = res.data.total;
+    }
+
+  })
+}
+const roleListfun = () => {
+  api.roleApi.roleList({ name: "" }).then((res: any) => {
+    if (res.code == '200') {
+      roleLists.value = res.data;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//添加
+const memberAdd = () => {
+  if (dialogType.value == 'add') {
+    api.member.userAdd(memberform).then((res: any) => {
+      if (res.code == '200') {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+        findpage();
+        UpdatedialogVisible.value = false;
+      } else {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+      }
+    });
+  } else {
+    console.log(memberform)
+    api.member.userEdit(memberform).then((res: any) => {
+      if (res.code == '200') {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+        findpage();
+        UpdatedialogVisible.value = false;
+      } else {
+        ElMessage({
+          type: 'success',
+          message: res.msg,
+          plain: true,
+        })
+      }
+    });
+  }
+
+
+}
+//交互事件===========================
+function handleEnter(e: any) {
+  if (e.keyCode == 13 || e.keyCode == 108) {
+    //登录接口逻辑
+    memberQuery()
+  }
+}
+const statusformatter = (row: any, column: any, cellValue: any) => {
+  let accountStatus = row.accountStatus;
+  if (accountStatus) {
+    return () => h(ElTag, { type: 'primary' }, '启用');
+  } else {
+    return () => h(ElTag, { type: 'danger' }, '禁用');
+  }
 };
-const getList = (item: PageInfo) => {
-  pageInfo.pageNum = item.pageNum;
-  pageInfo.pageSize = item.pageSize;
-};
-const editor = (item: any) => {
-  dialogVisible.value = true;
-  // pageInfo.pageNum=item.pageNum
-  // pageInfo.pageSize=item.pageSize
-};
-const add = (formEl: FormInstance | undefined) => {
-  dialogVisible.value = true;
-  if (!formEl) return;
-  formEl.resetFields();
-  // pageInfo.pageNum=item.pageNum
-  // pageInfo.pageSize=item.pageSize
+const roleNameformatter = (row: any, column: any, cellValue: any) => {
+  let roleNameList = row.roleNameList;
+  let roleName = roleNameList.join(',');
+  return () => h('div', {}, roleName);
 };
+const columns = [
+  { prop: "username", label: "姓名", width: '120', },
+  { prop: "mobile", label: "手机号", width: '120' },
+  { prop: "id", label: "工号", width: '180', },
+  { prop: "roleNameList", label: "角色",formatter: roleNameformatter },
+  { prop: "accountStatus", label: "状态", formatter: statusformatter },
+
+];
 
-const operationMany = (item: any) => {
-  // pageInfo.pageNum=item.pageNum
-  // pageInfo.pageSize=item.pageSize
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
 };
+function tenantUpdate(id: string) {
+  dialogTitle.value = '编辑成员';
+  dialogType.value = 'edit';
+  api.member.userGet({ id: id }).then((res: any) => {
+    if (res.code == '200') {
+      memberform.id = res.data.id;
+      memberform.username = res.data.username;
+      memberform.mobile = res.data.mobile;
+      memberform.roleNameList = res.data.roleNameList;
+      UpdatedialogVisible.value = true;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const handleSelectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+const selectChange = (val: any) => {
+  sysUpAndDownList.value = val;
 
+}
+//删除
+const del = (value: string) => {
+  ElMessageBox.confirm(
+    '账号删除后,该账号将无法使用访问本企业,请确定是否删除?',
+    '确定要删除账号吗?',
+    {
+      confirmButtonText: '确认删除',
+      cancelButtonText: '取消',
+      type: 'error',
+      customClass: 'ElMessageBoxClass'
+    }
+  )
+    .then(() => {
+      api.member.userDelete([value]).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          findpage();
+        } else {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        }
+      });
+    })
+    .catch(() => {
+
+    })
+
+}
+
+//禁用
+const disable = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要禁用账号吗?',
+    '禁用账号',
+    {
+      confirmButtonText: '确认禁用',
+      cancelButtonText: '取消',
+      type: 'error',
+      customClass: 'ElMessageBoxClass'
+    }
+  )
+    .then(() => {
+      sysUpAndDown(0, [value]);
+    })
+    .catch(() => {
+
+    })
+}
+//启用
+const enable = (value: string) => {
+  sysUpAndDown(1, [value]);
+
+}
+//批量删除
+const batchDel = () => {
+  ElMessageBox.confirm(
+    '账号删除后,该账号将无法使用访问本企业,请确定是否删除?',
+    '确定要删除账号吗?',
+    {
+      confirmButtonText: '确认删除',
+      cancelButtonText: '取消',
+      type: 'error',
+      customClass: 'ElMessageBoxClass'
+    }
+  )
+    .then(() => {
+      api.member.userDelete(sysUpAndDownList.value).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          findpage();
+
+        } else {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        }
+      });
+    })
+    .catch(() => {
+
+    })
+}
+//批量禁用
+const batchdisable = () => {
+  ElMessageBox.confirm(
+    '确定要禁用账号吗?',
+    '禁用账号',
+    {
+      confirmButtonText: '确认禁用',
+      cancelButtonText: '取消',
+      type: 'error',
+      customClass: 'ElMessageBoxClass'
+    }
+  )
+    .then(async () => {
+      await sysUpAndDown(0, undefined);
+    })
+    .catch(() => {
+
+    })
 
-const reset = (item: any) => {
-  // ElMessage({})
-};
 
+}
+//批量启用
+const batchenable = async () => {
+  await sysUpAndDown(1, undefined);
+}
+const memberQuery = () => {
+  findpage();
+}
+const sysUpAndDown = async (type: number, list?: string[]) => {
+  await api.member.upAndDown({ ids: list ? list : sysUpAndDownList.value, opType: type }).then((res: any) => {
+    if (res.code == '200') {
+      findpage();
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+//添加子机构
+// const subOrganizationAdd = (code: string) => {
+//   router.push({
+//     name: 'OrganizationDeptAdd',
+//     query: {
+//       code: code
+//     }
+//   })
+// }
+const resetForm = () => {
+  formInline.searchValue = '';
+  formInline.isEnable = "";
+}
+const userAdd = () => {
+  dialogTitle.value = '添加成员';
+  dialogType.value = 'add';
+  memberform.id = "";
+  UpdatedialogVisible.value = true;
+}
 </script>
+
 <style lang="scss" scoped>
-.member {
-  background: #fff;
-  width: 100%;
-  // height: 100%;
-  padding: 20px;
-  box-sizing: border-box;
-}
 :deep(.el-dropdown-link) {
-  cursor: pointer;
-  color: var(--el-color-primary);
   display: flex;
   align-items: center;
   margin-left: 15px;
+  color: var(--el-color-primary);
+  cursor: pointer;
+}
+
+:deep(.el-divider--horizontal) {
+  margin: 8px 0 24px;
+  color: #f2f2f2;
+}
+
+.el-inputs {
+  width: 100% !important;
 }
 </style>

+ 107 - 59
src/views/personnel/roleManagement.vue

@@ -1,64 +1,84 @@
 <template>
   <div>
-    <ElRow :gutter="20">
-      <ElCol :md="6">
-        <PageMain class="vh100">
-          <ElFormItem prop="roleName">
-            <ElCol :md="17">
-              <ElInput v-model="roleName" placeholder="角色名称查找" type="text" tabindex="1">
+    <PageMain class="vh100">
+      <ElRow :gutter="20" style="height: 100%;">
+        <ElCol :md="5">
+          <div class="flex f-c j-s" style="height: 100%;">
+            <ElFormItem prop="roleName">
+              <ElInput v-model="roleName" placeholder="角色名称查找" type="text" tabindex="1" style="width: 100%;">
                 <template #prefix>
                   <SvgIcon name="i-ri:user-3-fill" />
                 </template>
+                <template #append>
+                  <el-button :icon="Search" class="ElInputSearch" @click="rolequery" />
+                </template>
               </ElInput>
-            </ElCol>
-            <ElCol :md="6">
-              <ElButton type="primary" @click="rolequery">查找</ElButton>
-            </ElCol>
-          </ElFormItem>
-          <div style="height: 500px;overflow-y: auto;">
-            <div class="pad-2 roleitem flex f-c " v-for="(item, index) in roleList" :key="index"
-              @click="roleitemClick(item)">
-              <span>{{ item.name }}</span>
-              <span style="margin-top: 10px;font-size: 12px;color: #999;">{{ item.remark }}</span>
+            </ElFormItem>
+            <div style=" width: 100%;height: calc(100vh - 350px);  overflow-y: auto;" class="container">
+              <div class="pad-1 roleitem flex f-c a-start" :class="{ active: roleId === item.id }"
+                v-for="(item, index) in roleList" :key="index" @click="roleitemClick(item)">
+                <span>{{ item.name }}</span>
+                <span style="margin-top: 10px;font-size: 12px;color: #999;">{{ item.remark }}</span>
+              </div>
+            </div>
+            <div class="flex a-c j-c footer">
+              <el-button type="primary" plain style="margin: auto;" @click="resetForm">
+                <el-icon>
+                  <Plus />
+                </el-icon>
+                新增角色</el-button>
             </div>
           </div>
-          <PaginationWrapper v-if="totalSize > 0" style="position: absolute;bottom: 0;" :pageNum="PageNum"
-            :pageSize="PageSize" @pageChange="pageChange" @sizeChange="sizeChange" :total="totalSize" />
-        </PageMain>
-      </ElCol>
-      <ElCol :md="18">
-        <PageMain class="vh100">
-          <template #title>
-            <div class="flex j-s a-c">
-              <span class="strong">角色管理</span>
-              <div>
-                <ElButton type="primary" @click="roleSubmit">提交</ElButton>
-                <ElButton v-if="roleInfo.id" type="danger" @click="roleDelClick">删除</ElButton>
-              </div>
+        </ElCol>
+        <ElCol :md="1">
+          <ElDivider direction="vertical" style="height: 100%;"></ElDivider>
+        </ElCol>
+        <ElCol :md="18">
+          <div class="flex j-s a-c">
+            <span class="strong">角色管理</span>
+            <div>
+              <ElButton type="default" @click="roleSubmit">保存</ElButton>
+              <ElButton v-if="roleInfo.id" type="default" @click="roleDelClick">删除</ElButton>
             </div>
-          </template>
-          <h5>基本信息</h5>
-          <ElCol :md="6"></ElCol>
-          <ElForm :model="roleInfo" :rules="roleRules" label-width="auto" size="" style="max-width: 500px;">
-            <ElFormItem prop="name" label="角色名称">
-              <ElInput v-model="roleInfo.name" placeholder="输入20个字以内的角色名称" type="text" tabindex="1">
-              </ElInput>
-            </ElFormItem>
-            <ElFormItem label="角色描述">
-              <ElInput v-model="roleInfo.remark" placeholder="请输入" type="textarea" tabindex="2">
-              </ElInput>
-            </ElFormItem>
-          </ElForm>
-          <ElDivider></ElDivider>
+          </div>
+          <ElDivider style="margin-top: 10px;"></ElDivider>
+          <div v-show="roleId || identifier">
+            <span style="font-size: 16px;font-weight: bold;">基本信息</span>
+            <ElForm :model="roleInfo" :rules="roleRules" label-width="auto" size="" class="m-top-20"
+              style="max-width: 500px;">
+              <ElFormItem prop="name" label="角色名称">
+                <ElInput v-model="roleInfo.name" placeholder="输入20个字以内的角色名称" type="text" tabindex="1">
+                </ElInput>
+              </ElFormItem>
+              <ElFormItem label="角色描述">
+                <ElInput v-model="roleInfo.remark" placeholder="请输入" type="textarea" tabindex="2">
+                </ElInput>
+              </ElFormItem>
+            </ElForm>
+            <ElDivider></ElDivider>
+            <div>
+              <h5>角色权限</h5>
+              <ElTree style="max-height: 350px;overflow-y: auto;" ref="treeRef" :data="treeData" :props="defaultProps"
+                show-checkbox node-key="id" :default-expand-all="true" :default-checked-keys="checkedKeys"
+                @check="handleCheckChange" />
+            </div>
+          </div>
+
+        </ElCol>
+      </ElRow>
+
+    </PageMain>
+    <!-- <template #title>
+        <div class="flex j-s a-c">
+          <span class="strong">角色管理</span>
           <div>
-            <h5>角色权限</h5>
-            <ElTree style="max-height: 350px;overflow-y: auto;" ref="treeRef" :data="treeData" :props="defaultProps"
-              show-checkbox node-key="id" :default-expand-all="true" :default-checked-keys="checkedKeys"
-              @check="handleCheckChange" />
+            <ElButton type="primary" @click="roleSubmit">提交</ElButton>
+            <ElButton v-if="roleInfo.id" type="danger" @click="roleDelClick">删除</ElButton>
           </div>
-        </PageMain>
-      </ElCol>
-    </ElRow>
+        </div>
+      </template> -->
+
+
 
   </div>
 </template>
@@ -66,8 +86,9 @@
 <script lang='ts' setup>
 import { asyncRoutes } from '@/router/routes' // 请根据实际路径调整
 import { ref, reactive, onBeforeMount } from 'vue';
-import { FormRules, ElTree, ElMessage, ElMessageBox  } from 'element-plus';
+import { FormRules, ElTree, ElMessage, ElMessageBox } from 'element-plus';
 import { preprocessRoutes, convertToTreeData, TreeNode } from '@/utils/composables/routeUtils'; // 导入工具方法
+import { Search } from '@element-plus/icons-vue'
 import api from '@/api'
 import Message from 'vue-m-message'
 
@@ -76,6 +97,7 @@ onBeforeMount(() => {
   // 对 asyncRoutes 进行预处理并转换为树形结构
   const processedRoutes = preprocessRoutes(asyncRoutes);
   treeData.value = convertToTreeData(0, processedRoutes);
+  console.log(treeData.value)
 });
 interface roleList {
   id: string,
@@ -94,7 +116,7 @@ const PageNum = ref<number>(1);
 const PageSize = ref<number>(20);
 const checkedKeys = ref<string[]>([])
 const totalSize = ref(0)
-
+const identifier = ref(false)
 const roleInfo = reactive<{
   name: string;
   remark: string;
@@ -223,12 +245,12 @@ async function roleitemClick(item: { id: string }) {
   })
 }
 async function rolequery() {
-  await api.roleApi.roleList({ pageNum: PageNum.value, pageSize: PageSize.value, name: roleName.value }).then((res: any) => {
-    totalSize.value = res.data.total;
-    roleList.value = res.data.records;
+  await api.roleApi.roleList({  name: roleName.value }).then((res: any) => {
+    roleList.value = res.data;
   })
 }
 function resetForm() {
+  identifier.value = true;
   roleInfo.name = "";
   roleInfo.remark = "";
   roleInfo.system = "PLATFORM";
@@ -245,21 +267,47 @@ function resetForm() {
 
 <style lang="scss" scoped>
 .roleitem {
-  cursor: pointer;
-  background: white;
+  span:first-child {
+    font-size: 16px;
+    color: #333;
+  }
+
+  span:last-child {
+    font-size: 14px;
+    color: #666;
+  }
 }
 
 .roleitem:hover {
-  background: #b4b2b2;
+  background: #f4faff;
   transition: 0.2s;
 }
 
 .active {
-  background: #b4b2b2;
+  background: #f4faff;
+  border-right: 2px solid #0083ff;
 }
 
 .el-pagination {
   position: absolute;
   bottom: 0;
 }
+
+.ElInputSearch {
+  color: #fff !important;
+  background: linear-gradient(91deg, #4fcaff 0%, #2b62ff 100%) !important;
+  border-color: #4fcaffe8 !important;
+  border-top-left-radius: 0 !important;
+  border-bottom-left-radius: 0 !important;
+}
+
+.footer {
+  width: 100%;
+}
+
+.container::-webkit-scrollbar {
+  display: none;
+
+  /* 针对 Webkit 浏览器 (如 Chrome/Safari) */
+}
 </style>

+ 4 - 4
themes/index.ts

@@ -66,13 +66,13 @@ export const lightTheme = {
   '--g-main-sidebar-menu-active-bg': '#FFFFFF',
   '--g-main-sidebar-menu-active-color': '#0376F9',
   // 次导航
-  '--g-sub-sidebar-bg': '#262930',
+  '--g-sub-sidebar-bg': '#fff',
   '--g-sub-sidebar-logo-bg': '#0376F9',
   '--g-sub-sidebar-logo-color': '#FFFFFF',
-  '--g-sub-sidebar-menu-color': 'rgba(255,255,255,0.65)',
+  '--g-sub-sidebar-menu-color': '#666666',
   '--g-sub-sidebar-menu-hover-bg': '#E6F0FF',
-  '--g-sub-sidebar-menu-hover-color': '#0376F9',
-  '--g-sub-sidebar-menu-active-bg': '#0376F9',
+  '--g-sub-sidebar-menu-hover-color': '#000',
+  '--g-sub-sidebar-menu-active-bg': 'linear-gradient( 91deg, #4FCAFF 0%, #2B62FF 100%)',
   '--g-sub-sidebar-menu-active-color': '#FFFFFF',
   // 标签栏
   '--g-tabbar-dividers-bg': '#E0E0E0',