Преглед изворни кода

Merge remote-tracking branch 'origin/DK-dev' into wuguojie

wuguojie пре 1 година
родитељ
комит
3367942b8a
81 измењених фајлова са 7697 додато и 941 уклоњено
  1. 4 2
      .env.development
  2. 2 2
      .env.production
  3. 2 2
      .env.test
  4. 114 33
      package-lock.json
  5. 4 0
      package.json
  6. 121 16
      pnpm-lock.yaml
  7. 16 5
      src/api/index.ts
  8. 8 11
      src/api/modules/app.ts
  9. 28 0
      src/api/modules/configurate.ts
  10. 22 0
      src/api/modules/dept.ts
  11. 32 0
      src/api/modules/insuranceCompany.ts
  12. 19 0
      src/api/modules/member.ts
  13. 18 0
      src/api/modules/product.ts
  14. 4 5
      src/api/modules/role.ts
  15. 39 0
      src/api/modules/tenant.ts
  16. 13 9
      src/api/modules/user.ts
  17. 27 0
      src/api/types/deptTypes.ts
  18. 223 0
      src/api/types/insuranceCompanyTypes.ts
  19. 27 0
      src/api/types/memberTypes.ts
  20. 170 0
      src/api/types/productTypes.ts
  21. 5 0
      src/assets/icons/arrow-right.svg
  22. 1 0
      src/assets/icons/caretRight.svg
  23. 5 0
      src/assets/icons/user.svg
  24. 6 0
      src/assets/icons/zh.svg
  25. BIN
      src/assets/images/decoration1.png
  26. BIN
      src/assets/images/decoration2.png
  27. BIN
      src/assets/images/decoration3.png
  28. BIN
      src/assets/images/empty.png
  29. BIN
      src/assets/images/titleImg.png
  30. BIN
      src/assets/images/user/avatar1.png
  31. BIN
      src/assets/images/user/avatar2.png
  32. BIN
      src/assets/images/user/avatar3.png
  33. BIN
      src/assets/images/user/avatar4.png
  34. 66 0
      src/assets/styles/globals.css
  35. 5 4
      src/components/LoginForm/index.vue
  36. 4 1
      src/components/PageMain/index.vue
  37. 255 177
      src/components/Table/index.vue
  38. 209 0
      src/components/TableTree/index.vue
  39. 208 0
      src/components/tablev2/index.vue
  40. 19 13
      src/layouts/components/Header/index.vue
  41. 90 68
      src/layouts/components/HotkeysIntro/index.vue
  42. 8 4
      src/layouts/components/Logo/index.vue
  43. 3 0
      src/layouts/components/Menu/sub.vue
  44. 125 20
      src/layouts/components/Topbar/Toolbar/rightSide.vue
  45. 10 4
      src/layouts/ui-kit/HDropdownMenu.vue
  46. 42 0
      src/router/modules/insuranceCompany.ts
  47. 0 37
      src/router/modules/multilevel.menu.example.ts
  48. 68 11
      src/router/modules/organization.ts
  49. 28 6
      src/router/modules/personnel.ts
  50. 56 0
      src/router/modules/product.ts
  51. 7 39
      src/router/routes.ts
  52. 2 2
      src/settings.ts
  53. 4 6
      src/store/modules/menu.ts
  54. 25 26
      src/store/modules/route.ts
  55. 33 14
      src/store/modules/user.ts
  56. 6 0
      src/types/auto-imports.d.ts
  57. 2 0
      src/types/components.d.ts
  58. 35 0
      src/utils/composables/dictService.ts
  59. 23 0
      src/utils/composables/imageUtils.ts
  60. 30 0
      src/utils/composables/pinyinHelper.ts
  61. 56 10
      src/utils/composables/routeUtils.ts
  62. 0 15
      src/views/example/ceshi.vue
  63. 0 52
      src/views/example/index.vue
  64. 551 0
      src/views/insuranceCompanyConfiguration/accountOwnershipConfig.vue
  65. 504 0
      src/views/insuranceCompanyConfiguration/insuranceCompanyConfig.vue
  66. 324 0
      src/views/insuranceCompanyConfiguration/paymentCodeConfig.vue
  67. 343 0
      src/views/organizationManagement/functionConfiguration.vue
  68. 135 0
      src/views/organizationManagement/modules/companySelect.vue
  69. 0 11
      src/views/organizationManagement/organization.vue
  70. 419 0
      src/views/organizationManagement/organization/organizationAdd.vue
  71. 458 0
      src/views/organizationManagement/organization/organizationDetail.vue
  72. 421 0
      src/views/organizationManagement/organization/organizationlist.vue
  73. 290 0
      src/views/organizationManagement/organization/subOrganizatioAdd.vue
  74. 223 0
      src/views/organizationManagement/tenantPermissions.vue
  75. 78 0
      src/views/personnel/memberAdd.vue
  76. 449 256
      src/views/personnel/memberManagement.vue
  77. 117 76
      src/views/personnel/roleManagement.vue
  78. 494 0
      src/views/productConfiguration/drivingInsurance/drivingInsuranceAdd.vue
  79. 240 0
      src/views/productConfiguration/drivingInsurance/index.vue
  80. 318 0
      src/views/productConfiguration/specialClause/index.vue
  81. 4 4
      themes/index.ts

+ 4 - 2
.env.development

@@ -3,8 +3,9 @@ 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  #田智
+# VITE_APP_API_BASEURL = http://192.168.0.59:8001  #徐强
 
 # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
 VITE_APP_DEBUG_TOOL =
@@ -15,3 +16,4 @@ VITE_APP_DISABLE_DEVTOOL = false
 VITE_OPEN_PROXY = false
 # 是否开启开发者工具
 VITE_OPEN_DEVTOOLS = false
+

+ 2 - 2
.env.production

@@ -1,13 +1,13 @@
 # 应用配置面板
 VITE_APP_SETTING = false
 # 页面标题
-VITE_APP_TITLE = 生产环境
+VITE_APP_TITLE = 掌柜运营平台
 # 接口请求地址,会设置到 axios 的 baseURL 参数上
 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

+ 2 - 2
.env.test

@@ -1,9 +1,9 @@
 # 应用配置面板
 VITE_APP_SETTING = false
 # 页面标题
-VITE_APP_TITLE = 测试环境
+VITE_APP_TITLE = 掌柜运营平台
 # 接口请求地址,会设置到 axios 的 baseURL 参数上
-VITE_APP_API_BASEURL = https://test.baoxianzhanggui.com/web-api
+VITE_APP_API_BASEURL = http://test-platform.baoxianzhanggui.com/api #测试地址
 # 调试工具,可设置 eruda 或 vconsole,如果不需要开启则留空
 VITE_APP_DEBUG_TOOL =
 # 是否禁用开发者工具,可防止被调试

+ 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: {}

+ 16 - 5
src/api/index.ts

@@ -5,10 +5,15 @@ 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'
+import company from './modules/insuranceCompany'
+import product from "./modules/product"
+import configurate from './modules/configurate.ts'
 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,
   timeout: 1000 * 60,
@@ -89,8 +94,14 @@ 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),
+  company:company(axiosInstance),
+  product:product(axiosInstance),
+  configurate: configurate(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

+ 28 - 0
src/api/modules/configurate.ts

@@ -0,0 +1,28 @@
+import { ApiInstance } from '../type'
+
+export interface SaveBusPerms {
+  companyId: string,
+  systemCode: string | number,
+  quoteCompany: string | number,
+  orderMode: string | number,
+  orderAuditDept: string | number,
+  salesmanAuditDept: string | number,
+  salesmanWithdrawAccountAuditDept: string | number,
+  salesmanWithdrawPaymentAuditDept: string | number,
+  id?: string,
+  quoteCompanyList?: string[],
+  orderAuditDeptList?: string[],
+  salesmanAuditDeptList?: string[],
+  salesmanWithdrawAccountAuditDeptList: string[],
+  salesmanWithdrawPaymentAuditDeptList: string[]
+}
+
+const configurateApi = (axiosInstance: ApiInstance) => ({
+  getDeptListBySystemCode: (data: string) => axiosInstance.get(`/sysDept/getDeptListBySystemCode?systemCode=${data}`),
+  liveCompanyList: () => axiosInstance.get('/esmInsCompany/liveCompanyList'), // 保司
+  saveBusPerms: (data: SaveBusPerms) => axiosInstance.post('/system/saveBusPerms', data), // 保存
+  getBusPerms: (data: { systemCode: string, companyId: string }) => axiosInstance.get(`/system/getBusPerms?systemCode=${data.systemCode}&&companyId=${data.companyId}`)
+})
+
+export default configurateApi;
+

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

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

+ 32 - 0
src/api/modules/insuranceCompany.ts

@@ -0,0 +1,32 @@
+import { ApiInstance } from '../type'
+import * as Types from '../types/insuranceCompanyTypes';
+const company = (axiosInstance: ApiInstance) => ({
+
+  //获取保险公司数结构
+  findTree: (data: Types.companyTreeRequest) => axiosInstance.post('esmInsCompany/findTree', data),
+  //添加保险公司
+  saveInsCompany: (data: Types.info) => axiosInstance.post('esmInsCompany/saveInsCompany', data),
+  // 修改
+  editInsCompany: (data: Types.info) => axiosInstance.post('esmInsCompany/editInsCompany', data),
+  // 删除
+  CompanyDel: (id: String) => axiosInstance.get(`esmInsCompany/${id}`),
+  //查询一级列表
+  selectLevelList: (data: Types.companyTreeRequest) => axiosInstance.post('esmInsCompany/selectLevelList', data),
+  //账号归属模版配置
+  attributionTemplate: (data: Types.attributionRequest) => axiosInstance.post('manager/attributionTemplate/submit', data),
+  //获取账号归属配置
+  attributionTemplatequery: (data: Types.attributionRequest) => axiosInstance.post('manager/attributionTemplate/getList', data),
+  // 二维码保存配置
+  QRBarcodeAdd: (data: Types.qrBarcodeRequest) => axiosInstance.post('qrBarcode/save', data),
+  //二维码配置修改
+  QRBarcodeUpdate: (data: Types.qrBarcodeRequest) => axiosInstance.post('qrBarcode/updateById', data),
+  //二维码配置分页查询
+  QRcodePage: (data: Types.pageRequest) => axiosInstance.post('/qrBarcode/queryPage', data),
+  //二维码配置查询
+  QRcodeById: (id: string) => axiosInstance.get(`qrBarcode/queryById/${id}`),
+  //二维码配置分页查询
+  QRcodeDel: (id: string) => axiosInstance.get(`qrBarcode/delete/${id}`),
+
+})
+
+export default company

+ 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

+ 18 - 0
src/api/modules/product.ts

@@ -0,0 +1,18 @@
+import { ApiInstance } from '../type'
+import * as Types from '../types/productTypes';
+const product = (axiosInstance: ApiInstance) => ({
+  //保存驾意险配置
+  jyxConfigAdd: (data: Types.jyxConfigRequest) => axiosInstance.post('/jyxConfig/saveAll', data),
+  //查询主险
+  selectMainInsurance: (company:string ) => axiosInstance.get(`/jyxConfig/selectMainInsurance?company=${company}`),
+  //查询驾意险列表
+  jyxList: (data: Types.jyxListRequest) => axiosInstance.post('/jyxConfig/jyxList', data),
+  //详情
+  jygetById: (id:string ) => axiosInstance.get(`/jyxConfig/getById?id=${id}`),
+  //驾意险修改
+  jyxupdateById: (data: Types.jyxConfigRequest) => axiosInstance.post('/jyxConfig/updateById', data),
+  //删除
+  deleteById: (id:string ) => axiosInstance.get(`/jyxConfig/deleteById?id=${id}`),
+})
+
+export default product

+ 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, {}),

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

@@ -0,0 +1,39 @@
+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),
+    // 租户全部列表
+    sysListAll: (data: sysList) => axiosInstance.post('system/sysListAll', 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: (params: any) => axiosInstance.get('system/getSystemPerms', {params}),
+  //修改租户权限
+  updatePerms: (data: any) => axiosInstance.post('system/updatePerms', data),
+  //获取租户平台菜单数据
+  getSystemConfig: (params:any) => axiosInstance.get('system/getInitConfig',{params}),
+  // 获取租户菜单权限
+  getTenantConfig: (params:any) => axiosInstance.get('system/getInitConfig',{params}),
+  //租户手机号校验
+  userGetPhone: (params:any) => axiosInstance.get(`system/userGet?mobile=${params}`)
+})
+
+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;
+}

+ 223 - 0
src/api/types/insuranceCompanyTypes.ts

@@ -0,0 +1,223 @@
+
+/**
+ * EsmInsCompanyReq,保险公司
+ */
+export type companyTreeRequest = {
+  /**
+   * 名称
+   */
+  name?: string;
+  /**
+   * 供应商类型
+   */
+  type?: string;
+  [property: string]: any;
+}
+
+export type info = {
+  /**
+   * 拼音
+   */
+  cnName?: string;
+  /**
+   * 编码
+   */
+  code: string;
+  /**
+   * 保险公司logo
+   */
+  logo?: string;
+  /**
+   * 名称
+   */
+  name: string;
+  /**
+   * 名称简称
+   */
+  nameSimple: string;
+  /**
+   * 排序
+   */
+  orderNum?: number;
+  /**
+   * 父编号
+   */
+  parentId?: string;
+  /**
+   * 备注
+   */
+  remark?: string;
+  /**
+   * 保司类型(1 - 财险、2 - 寿险、0 - 渠道)
+   */
+  type: number | string;
+  [property: string]: any;
+}
+
+export type attributionRequest = {
+  /**
+   * api类型为2(python)必传
+   */
+  /**
+   * api类型
+   */
+  apiType?: number;
+  /**
+   * 归属配置
+   */
+  attributionConfig?: string;
+  /**
+   * 字段集
+   */
+  fields?: PtlAttributionTemplateDto[];
+  /**
+   * id
+   */
+  id?: string;
+  /**
+   * 影像上传
+   */
+  imageUpload?: string;
+  /**
+   * 保险公司ID
+   */
+  insCompanyId?: string;
+  /**
+   * 核保
+   */
+  insurance?: string;
+  /**
+   * 人工核保状态刷新
+   */
+  manualStatusRefresh?: string;
+  /**
+   * 非车险查询
+   */
+  nonCarInsurance?: string;
+  /**
+   * 缴费查询
+   */
+  paymentQuery?: string;
+  /**
+   * 保单下载
+   */
+  policyDownload?: string;
+  /**
+   * 报价
+   */
+  quotation?: string;
+  /**
+   * 撤销订单
+   */
+  revokeOrder?: string;
+  [property: string]: any;
+}
+/**
+ * com.jzg.commons.entity.agreement.dto.PtlAttributionTemplateDto
+ *
+ * PtlAttributionTemplateDto,归属配置模版
+ */
+export type PtlAttributionTemplateDto = {
+  /**
+   * 字段
+   */
+  field?: string;
+  /**
+   * 是否允许修改(true 可修改,false 不可修改)
+   */
+  isEdit?: boolean;
+  /**
+   * 标签
+   */
+  label?: string;
+  /**
+   * 类型(text 文本框,select 选择框)
+   */
+  type?: string;
+  /**
+   * 值
+   */
+  value?: string;
+  [property: string]: any;
+}
+/**
+ * QrBarcodeConfigAdd,二维码配置新增
+ */
+export type qrBarcodeRequest = {
+  /**
+    * 主键
+    */
+  id?: string;
+  /**
+   * 请求类型 1:api 2:python
+   */
+  apiType: number | string;
+  /**
+   * 保险公司id
+   */
+  companyId: string;
+  /**
+   * 保险公司名称简称
+   */
+  companyName: string;
+  systemCode?: string;
+  /**
+   * 类型 0:url   1:base码   2:h5
+   */
+  type: number | string;
+  [property: string]: any;
+}
+/**
+ * QrBarcodeConfigParam,二维码配置查询参数
+ */
+export type pageRequest = {
+  /**
+   * 保险公司
+   */
+  companyName?: string;
+  page?: Page;
+  pages?: number;
+  size?: number;
+  [property: string]: any;
+}
+export type Page = {
+  countId?: string;
+  current?: number;
+  maxLimit?: number;
+  optimizeCountSql?: PageObject;
+  optimizeJoinOfCountSql?: boolean;
+  orders?: OrderItem[];
+  pages?: number;
+  records?: { [key: string]: any }[];
+  searchCount?: PageObject;
+  size?: number;
+  total?: number;
+  [property: string]: any;
+}
+
+/**
+ * PageObject
+ */
+export type PageObject = {
+  countId?: string;
+  current?: number;
+  maxLimit?: number;
+  optimizeCountSql?: PageObject;
+  optimizeJoinOfCountSql?: boolean;
+  orders?: OrderItem[];
+  pages?: number;
+  records?: { [key: string]: any }[];
+  searchCount?: PageObject;
+  size?: number;
+  total?: number;
+  [property: string]: any;
+}
+
+/**
+* OrderItem
+*/
+export type OrderItem = {
+  asc?: boolean;
+  column?: 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;
+}

+ 170 - 0
src/api/types/productTypes.ts

@@ -0,0 +1,170 @@
+/**
+ * JyxConfigAdd,驾意险配置
+ */
+export type jyxConfigRequest = {
+  /**
+   * 别名
+   */
+  alias?: string;
+  /**
+   * id
+   */
+  id?: string;
+  /**
+   * 保险责任
+   */
+  blameList: JyxBlame[];
+  /**
+   * 保险公司
+   */
+  company: string;
+  /**
+   * 对接方式
+   */
+  connectionMethod: string;
+  /**
+   * 总保额
+   */
+  coverage: string;
+  /**
+   * 上级产品
+   */
+  parentId: string;
+  /**
+   * 总保费
+   */
+  premium: string;
+  /**
+   * 保费设置
+   */
+  premiumSetting: string;
+  /**
+   * 产品编码
+   */
+  productCode: string;
+  /**
+   * 产品名称
+   */
+  productName: string;
+  /**
+   * 产品类型
+   */
+  productType: string;
+  /**
+   * 驾意险保费设置
+   */
+  settingList: JyxSetting[];
+  [property: string]: any;
+}
+
+/**
+* JyxBlame,驾意险责任配置
+*/
+export type JyxBlame = {
+  /**
+   * 保险责任
+   */
+  blame?: string;
+  /**
+   * 责任编码
+   */
+  blameCode?: string;
+  /**
+   * 责任保额
+   */
+  blameCoverage?: number;
+  /**
+   * 责任保费
+   */
+  blamePremium?: number;
+  /**
+   * 创建人
+   */
+  createBy?: string;
+  /**
+   * 创建时间
+   */
+  createTime?: Date;
+  id?: string;
+  /**
+   * 是否删除
+   */
+  isDelete?: number;
+  jyxConfigId?: string;
+  systemCode?: string;
+  /**
+   * 修改人
+   */
+  updateBy?: string;
+  /**
+   * 修改时间
+   */
+  updateTime?: Date;
+  [property: string]: any;
+}
+
+/**
+* jyxSetting,驾意险保费设置
+*/
+export type JyxSetting = {
+  /**
+   * 保额
+   */
+  coverage?: number;
+  /**
+   * 创建人
+   */
+  createBy?: string;
+  /**
+   * 创建时间
+   */
+  createTime?: Date;
+  id?: string;
+  /**
+   * 是否删除
+   */
+  isDelete?: number;
+  jyxConfigId?: string;
+  /**
+   * 保费
+   */
+  premium?: number;
+  /**
+   * 座位数
+   */
+  seats?: number;
+  systemCode?: string;
+  /**
+   * 修改人
+   */
+  updateBy?: string;
+  /**
+   * 修改时间
+   */
+  updateTime?: Date;
+  [property: string]: any;
+}
+export type jyxConfigAddRequest = {
+  company: string;
+  [property: string]: any;
+}
+/**
+ * JyxConfigParam,驾意险配置查询DTO
+ */
+export type jyxListRequest = {
+  /**
+   * 查询参数 保险公司
+   */
+  company?: string;
+  /**
+   * 查询参数 输入产品名称,别名查找
+   */
+  params?: string;
+  parentId?: string;
+  /**
+   * 查询参数 输入座位数
+   */
+  seats?: string;
+  size?: number;
+  [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>

+ 5 - 0
src/assets/icons/user.svg

@@ -0,0 +1,5 @@
+<svg width="16" height="18" viewBox="0 0 16 18" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
+<g id="ICON/sys/icon_sys_section">
+<path id="Vector" d="M14.1343 11.4272H13.2009V9.00036C13.2009 8.69138 12.9498 8.44031 12.6409 8.44031H8.72055V6.5735H9.65394C9.96294 6.5735 10.214 6.32244 10.214 6.01345V3.39989C10.214 3.09091 9.96294 2.83984 9.65394 2.83984H7.04041C6.73141 2.83984 6.48036 3.09091 6.48036 3.39989V6.01344C6.48036 6.32242 6.73141 6.57348 7.04041 6.57348H7.97381V8.4403H4.0535C3.7445 8.4403 3.49345 8.69136 3.49345 9.00034V11.4272H2.56003C2.25105 11.4272 2 11.6783 2 11.9873V14.6008C2 14.9098 2.25105 15.1609 2.56003 15.1609H5.17358C5.48258 15.1609 5.73362 14.9098 5.73362 14.6008V11.9873C5.73362 11.6783 5.48258 11.4272 5.17358 11.4272H4.24017V9.18705H7.97381V11.4272H7.04041C6.73141 11.4272 6.48036 11.6783 6.48036 11.9873V14.6008C6.48036 14.9098 6.73141 15.1609 7.04041 15.1609H9.65394C9.96294 15.1609 10.214 14.9098 10.214 14.6008V11.9873C10.214 11.6783 9.96294 11.4272 9.65394 11.4272H8.72053V9.18705H12.4542V11.4272H11.5208C11.2118 11.4272 10.9607 11.6783 10.9607 11.9873V14.6008C10.9607 14.9098 11.2118 15.1609 11.5208 15.1609H14.1343C14.4433 15.1609 14.6943 14.9098 14.6943 14.6008V11.9873C14.6943 11.6783 14.4433 11.4272 14.1343 11.4272V11.4272ZM7.22708 3.58658H9.46727V5.82677H7.22708V3.58661V3.58658ZM4.98691 12.1739V14.4141H2.7467V12.174H4.98689L4.98691 12.1739ZM9.46727 14.4141H7.22708V12.174H9.46727V14.4141ZM13.9476 14.4141H11.7074V12.174H13.9476L13.9476 14.4141Z" fill="currentColor"/>
+</g>
+</svg>

+ 6 - 0
src/assets/icons/zh.svg

@@ -0,0 +1,6 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame">
+<path id="Vector" d="M12.4106 9.9659C12.4458 9.92688 12.4729 9.88131 12.4904 9.83181C12.508 9.7823 12.5156 9.72982 12.5128 9.67737C12.5101 9.62492 12.497 9.57353 12.4744 9.52612C12.4518 9.47872 12.4201 9.43624 12.381 9.4011C11.6882 8.77671 10.8663 8.31264 9.97381 8.0419C10.9898 7.4171 11.6682 6.2955 11.6682 5.0179C11.6682 3.0619 10.077 1.4707 8.12101 1.4707C6.16501 1.4707 4.57461 3.0619 4.57461 5.0179C4.57461 6.2947 5.25301 7.4163 6.26901 8.0411C3.66101 8.8363 1.75781 11.2643 1.75781 14.1291C1.75781 14.3499 1.93701 14.5291 2.15781 14.5291C2.37861 14.5291 2.55781 14.3499 2.55781 14.1291C2.55781 11.0611 5.05381 8.5651 8.12181 8.5651C9.50021 8.5651 10.8226 9.0731 11.8458 9.9947C11.8848 10.0299 11.9303 10.0571 11.9798 10.0747C12.0293 10.0923 12.0817 10.1 12.1342 10.0973C12.1866 10.0946 12.238 10.0816 12.2855 10.0591C12.3329 10.0366 12.3754 10.0049 12.4106 9.9659ZM5.37461 5.0179C5.37461 3.5027 6.60741 2.2707 8.12181 2.2707C9.63621 2.2707 10.869 3.5027 10.869 5.0179C10.869 6.5331 9.63621 7.7651 8.12181 7.7651C6.60741 7.7651 5.37461 6.5331 5.37461 5.0179Z" fill="currentColor" stroke="currentColor" stroke-width="0.2"/>
+<path id="Vector_2" d="M9.29688 11.3219C9.29688 11.5427 9.47608 11.7219 9.69688 11.7219H13.8433C14.0641 11.7219 14.2433 11.5427 14.2433 11.3219C14.2433 11.1011 14.0641 10.9219 13.8433 10.9219H9.69688C9.47528 10.9219 9.29688 11.1011 9.29688 11.3219ZM13.8425 13.3939H9.69688C9.47608 13.3939 9.29688 13.5731 9.29688 13.7939C9.29688 14.0147 9.47608 14.1939 9.69688 14.1939H13.8433C14.0641 14.1939 14.2433 14.0147 14.2433 13.7939C14.2433 13.5731 14.0641 13.3939 13.8425 13.3939Z" fill="currentColor" stroke="currentColor" stroke-width="0.2"/>
+</g>
+</svg>

BIN
src/assets/images/decoration1.png


BIN
src/assets/images/decoration2.png


BIN
src/assets/images/decoration3.png


BIN
src/assets/images/empty.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


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

@@ -152,6 +152,61 @@ 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;
+}
+
+/* 消息提示弹窗 */
+.el-message-box {
+  padding: 32px 32px 24px;
+}
+
+.el-message-box .el-message-box__title {
+  gap: 17px;
+  align-items: center;
+  justify-content: flex-start;
+  font-weight: bold;
+}
+
+.el-message-box .el-message-box__title .el-icon {
+  font-size: x-large;
+}
+
+.el-message-box .el-message-box__title .el-message-box-icon--info {
+  color: #0083ff;
+}
+
+.el-message-box .el-message-box__btns {
+  justify-content: flex-end;
+  padding-top: 24px;
+}
+
+.el-message-box .el-message-box__content {
+  padding-left: 40px;
+}
+
+.el-message-box .el-message-box__content .el-message-box__container {
+  justify-content: flex-start;
+}
+
+/* 消息提示弹窗 */
+
+/* 全局样式覆盖 */
+
+/* 自定义全局样式 */
 .height100 {
   height: 100%;
 }
@@ -216,6 +271,10 @@ textarea {
   margin-top: 10px;
 }
 
+.m-top-20 {
+  margin-top: 20px;
+}
+
 .m-btn-10 {
   margin-bottom: 10px;
 }
@@ -230,9 +289,14 @@ textarea {
 }
 
 .strong {
+  font-size: 14px;
   font-weight: bold;
 }
 
+.elementSpacing > * {
+  margin-bottom: 5px; /* 下边距 */
+}
+
 .size14 {
   font-size: 14px;
 }
@@ -248,3 +312,5 @@ textarea {
 .pad-3 {
   padding: 30px;
 }
+
+/* 自定义全局样式 */

+ 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>

+ 255 - 177
src/components/Table/index.vue

@@ -1,191 +1,269 @@
-
 <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">
-						<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 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%;"
+        @selection-change="selectionChange" @select-all="selectAllChange">
+        <!-- 空内容自定义 -->
+        <template #empty>
+          <div class="flex f-c a-c ">
+            <img src="../../assets/images/empty.png" alt="">
+            <span style="font-size: 14px;color: #909399;">暂无数据</span>
+          </div>
+        </template>
+        <el-table-column type="selection" v-if="showSelect" :selectable="selectable" width="55" />
+        <el-table-column type="expand" v-if="secondaryTable">
+          <template #default="props">
+            <div>
+              <el-table :data="props.row.children">
+                <el-table-column v-for="column in secondaryColumns" :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">
+                    <RouterLink v-if="column.component === 'RouterLink'" :to="getRouterLinkTo(scope.row, column)"
+                      class="custom-link">
+                      {{ scope.row[column.prop] }}
+                    </RouterLink>
+                    <el-tag v-else-if="column.component === 'Tag'" :type="getTagTypeAndText(scope.row, column).type">
+                      {{ getTagTypeAndText(scope.row, column).text }}
+                    </el-tag>
+                    <ImagePreview v-else-if="column.component === 'Image'" :src="getImgTo(scope.row, column)"
+                      width="50px" height="50px"></ImagePreview>
+                    <span v-else>
+                      {{ getDisplayText(scope.row, column) }}
+                    </span>
+                  </template>
+                </el-table-column>
+                <el-table-column label="操作" fixed="right" :width="props.columnwidth">
+                  <template #default="scope">
+                    <slot name="secondary" :secondaryData="scope.row"></slot>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+
+          </template>
+        </el-table-column>
+        <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"
+          :align="column.align ? column.align : 'center'">
+          <template #header>
+            <slot name="search" :slotData="column"></slot>
+          </template>
+          <template #default="scope">
+            <RouterLink v-if="column.component === 'RouterLink'" :to="getRouterLinkTo(scope.row, column)"
+              class="custom-link">
+              {{ scope.row[column.prop] }}
+            </RouterLink>
+            <el-tag v-else-if="column.component === 'Tag'" :type="getTagTypeAndText(scope.row, column).type">
+              {{ getTagTypeAndText(scope.row, column).text }}
+            </el-tag>
+            <ImagePreview v-else-if="column.component === 'Image'" :src="getImgTo(scope.row, column)" width="50px"
+              height="50px"></ImagePreview>
+            <span v-else>
+              {{ getDisplayText(scope.row, column) }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" :width="props.columnwidth" align="center">
+          <template #default="scope">
+            <div class="flex a-c j-c">
+              <slot name="operation" :operationData="scope.row"></slot>
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div class="page_box" v-if="props.pageInfo">
+      <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;
+}
+const emit = defineEmits(['getList', 'selectAllChange', 'selectionChange']);
+const props = defineProps<{
+  tableData: Array<TableRow>;
+  columnwidth: number,//操作栏宽度
+  secondaryTable?: boolean,//是否展示二级表格
+  columns: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    align?: string,
+    component?: string,
+    formatter?: (row: any, column: any, cellValue: any, index: number) => any;
 
+  }[];
+  secondaryColumns?: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    component?: 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),
+}>();
+interface Row {
+  [key: string]: any; // 假设Row是一个具有任意属性的对象
+}
+interface RouterLink {
+  name: 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;
+      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 handleSizeChange = (val: number) => {
+  if (props.pageInfo) {
+    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 selectionChange = (value: any[]) => {
+  if (value.length > 0) {
+    value.map(val => {
+      newSelectionList.value.push(val.id)
+    })
+  } else {
+    newSelectionList.value = [];
+  }
+  emit('selectionChange', newSelectionList.value)
+}
+const handleCurrentChange = (val: number) => {
+  if (props.pageInfo) {
+    props.pageInfo.pageNum = val
+    emit('getList', props.pageInfo);
+  }
+
+}
+//空值处理
+const displayValue = (value: any) => {
+  return value === null || value === undefined || value === '' ? '-' : value;
+}
+// 格式化内容配置
+function getRouterLinkTo(row: Row, column: any): RouterLink {
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : { name: 'OrganizationAdd' };
+}
+function getImgTo(row: Row, column: any) {
+  return row[column.prop];
+}
+function getTagTypeAndText(row: Row, column: any) {
+  // 使用 formatter 返回一个对象,包含 type 和 text
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : { type: 'primary', text: row[column.prop] };
+}
+function getDisplayText(row: Row, column: any): string {
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : displayValue(row[column.prop]);
+}
 </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;
+.el-table .el-table__cell .routerlick > a {
+  color: red;
+}
+
+.containerTable {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: auto;
+}
+
+.custom-link {
+  color: #0083ff;
+
+  /* 设置文字颜色 */
+  text-decoration: none;
+
+  /* 去掉下划线 */
+}
+
+.table_box {
+  flex: 1;
+  margin-bottom: 10px;
+  overflow-y: auto;
+
+  .table_item_container {
+    padding: 0;
+    margin-left: 20px;
+
+    .table_item {
+      margin: 5px 0;
     }
-    .table-title-btn {
-      margin-bottom: 20px;
-      :deep(.el-button){
-        margin-right: 20px;
-      }
+  }
+
+  .table-title-btn {
+    margin-bottom: 20px;
+
+    :deep(.el-button) {
+      margin-right: 20px;
     }
-    
-	}
-  .page_box{
-    display: flex;
-    align-items: center;
-    justify-content: flex-end;
   }
-</style>
+}
+
+.page_box {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

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

@@ -0,0 +1,209 @@
+<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" v-loading="props.loading"
+        :header-cell-style="{ 'background-color': '#EEF1F6', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }"
+        :data="props.tableData" :height="tableHeight" row-key="id"
+         style="width: 100%;">
+        <!-- 空内容自定义 -->
+        <template #empty>
+          <div class="flex f-c a-c ">
+            <img src="../../assets/images/empty.png" alt="">
+            <span style="font-size: 14px;color: #909399;">暂无数据</span>
+          </div>
+        </template>
+        <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" :align="column.align?column.align:'center'" >
+          <template #default="scope">
+            <RouterLink v-if="column.component === 'RouterLink'" :to="getRouterLinkTo(scope.row, column)" class="custom-link">
+              {{ scope.row[column.prop] }}
+            </RouterLink>
+            <el-tag v-else-if="column.component === 'Tag'" :type="getTagTypeAndText(scope.row, column).type">
+              {{ getTagTypeAndText(scope.row, column).text }}
+            </el-tag>
+            <ImagePreview v-else-if="column.component === 'Image'" :src="getImgTo(scope.row, column)" width="50px" height="50px"></ImagePreview>
+            <span v-else>
+              {{ getDisplayText(scope.row, column) }}
+            </span>
+          </template>
+          <template #header>
+            <slot name="search" :slotData="column"></slot>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" :width="props.columnwidth" align="center">
+          <template #default="scope">
+            <div class="flex j-c a-c">
+              <slot name="operation" class="operationClass" :operationData="scope.row"></slot>
+            </div>
+          </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 Row {
+  [key: string]: any; // 假设Row是一个具有任意属性的对象
+}
+interface RouterLink {
+  name: string;
+}
+const emit = defineEmits([]);
+const props = defineProps<{
+  tableData: Array<TableRow>;
+  columnwidth: number,
+  loading?:boolean,
+  columns: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    align?:string,
+    component?: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 displayValue = (value: any) => {
+  return value === null || value === undefined || value === '' ? '-' : value;
+}
+const selectable = () => true;
+// 格式化内容配置
+function getRouterLinkTo(row: Row, column: any): RouterLink {
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : { name: 'OrganizationAdd' };
+}
+function getImgTo(row: Row, column: any) {
+  return row[column.prop];
+}
+function getTagTypeAndText(row: Row, column: any) {
+  // 使用 formatter 返回一个对象,包含 type 和 text
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : { type: 'primary', text: row[column.prop] };
+}
+function getDisplayText(row: Row, column: any): string {
+  return column.formatter ? column.formatter(row, column, row[column.prop]) :displayValue(row[column.prop]);
+}
+</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;
+    }
+  }
+
+  .table-title-btn {
+    margin-bottom: 20px;
+
+    :deep(.el-button) {
+      margin-right: 20px;
+    }
+  }
+}
+
+/* 自定义树状表格图标 */
+
+// /* 有子节点 且未展开 */
+// .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>

+ 208 - 0
src/components/tablev2/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" v-loading="props.loading"
+        :header-cell-style="{ 'background-color': '#EEF1F6', 'border-bottom': '1px solid #EEF1F6', 'font-size': '15px', 'color': '#333', 'padding': '10px 0' }"
+        :data="props.tableData" :height="tableHeight" row-key="id" border
+         style="width: 100%;">
+        <!-- 空内容自定义 -->
+        <template #empty>
+          <div class="flex f-c a-c ">
+            <el-empty image="../../../src/assets/images/empty.png" />
+          </div>
+        </template>
+        <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" :align="column.align?column.align:'center'" >
+          <template #default="scope">
+            <RouterLink v-if="column.component === 'RouterLink'" :to="getRouterLinkTo(scope.row, column)" class="custom-link">
+              {{ scope.row[column.prop] }}
+            </RouterLink>
+            <el-tag v-else-if="column.component === 'Tag'" :type="getTagTypeAndText(scope.row, column).type">
+              {{ getTagTypeAndText(scope.row, column).text }}
+            </el-tag>
+            <ImagePreview v-else-if="column.component === 'Image'" :src="getImgTo(scope.row, column)" width="50px" height="50px"></ImagePreview>
+            <span v-else>
+              {{ getDisplayText(scope.row, column) }}
+            </span>
+          </template>
+          <template #header>
+            <slot name="search" :slotData="column"></slot>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" fixed="right" :width="props.columnwidth" align="center">
+          <template #default="scope">
+            <div class="flex j-c a-c">
+              <slot name="operation" class="operationClass" :operationData="scope.row"></slot>
+            </div>
+          </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 Row {
+  [key: string]: any; // 假设Row是一个具有任意属性的对象
+}
+interface RouterLink {
+  name: string;
+}
+const emit = defineEmits([]);
+const props = defineProps<{
+  tableData: Array<TableRow>;
+  columnwidth: number,
+  loading?:boolean,
+  columns: {
+    prop: string;
+    label: string;
+    sortable?: boolean;
+    width?: string;
+    style?: string;
+    align?:string,
+    component?: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 displayValue = (value: any) => {
+  return value === null || value === undefined || value === '' ? '-' : value;
+}
+const selectable = () => true;
+// 格式化内容配置
+function getRouterLinkTo(row: Row, column: any): RouterLink {
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : { name: 'OrganizationAdd' };
+}
+function getImgTo(row: Row, column: any) {
+  return row[column.prop];
+}
+function getTagTypeAndText(row: Row, column: any) {
+  // 使用 formatter 返回一个对象,包含 type 和 text
+  return column.formatter ? column.formatter(row, column, row[column.prop]) : { type: 'primary', text: row[column.prop] };
+}
+function getDisplayText(row: Row, column: any): string {
+  return column.formatter ? column.formatter(row, column, row[column.prop]) :displayValue(row[column.prop]);
+}
+</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;
+    }
+  }
+
+  .table-title-btn {
+    margin-bottom: 20px;
+
+    :deep(.el-button) {
+      margin-right: 20px;
+    }
+  }
+}
+
+/* 自定义树状表格图标 */
+
+// /* 有子节点 且未展开 */
+// .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): 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>

+ 8 - 4
src/layouts/components/Logo/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import imgLogo from '@/assets/images/logo.png'
+// import imgLogo from '@/assets/images/logo.png'
 import useSettingsStore from '@/store/modules/settings'
 
 defineOptions({
@@ -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>

+ 125 - 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.realName); // 用户输入的中文文本
+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,92 @@ 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;
+  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>

+ 42 - 0
src/router/modules/insuranceCompany.ts

@@ -0,0 +1,42 @@
+import type { RouteRecordRaw } from 'vue-router'
+
+const Layout = () => import('@/layouts/index.vue')
+const routes: RouteRecordRaw = {
+  path: '/insuranceCompanyConfiguration',
+  component: Layout,
+  redirect: '/insuranceCompanyConfiguration/insuranceCompanyConfig',
+  name: 'InsuranceCompanyConfiguration',
+  meta: {
+    title: '保险公司配置',
+    icon:'zh',
+  },
+  children: [
+    {
+      path: 'insuranceCompanyConfig',
+      name: 'InsuranceCompanyConfigIndex',
+      component: () => import('@/views/insuranceCompanyConfiguration/insuranceCompanyConfig.vue'),
+      meta: {
+        title: '保险公司',
+        auth: ['insuranceCompanyConfig.add', 'insuranceCompanyConfig.view', 'insuranceCompanyConfig.delete', 'insuranceCompanyConfig.edit']
+      },
+    },
+    {
+      path: 'paymentCodeConfig',
+      name: 'PaymentCodeConfigIndex',
+      component: () => import('@/views/insuranceCompanyConfiguration/paymentCodeConfig.vue'),
+      meta: {
+        title: '支付码配置',
+      },
+    },
+    {
+      path: 'accountOwnershipConfig',
+      name: 'AccountOwnershipConfigIndex',
+      component: () => import('@/views/insuranceCompanyConfiguration/accountOwnershipConfig.vue'),
+      meta: {
+        title: '账号归属配置',
+      },
+    },
+  ],
+}
+
+export default routes

+ 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

+ 68 - 11
src/router/modules/organization.ts

@@ -1,26 +1,83 @@
 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: '租户管理',
+    icon:'zh',
   },
   children: [
     {
-      path: 'organization',
-      name: 'Organization',
-      component: () => import('@/views/organizationManagement/organization.vue'),
+      path: 'organizationlist',
+      name: 'Organizationlist',
+      redirect: '/organizationManagement/organization/organizationlist',
       meta: {
-        title: '机构管理',
+        title: '租户列表',
+        auth: ['organizationlist.add', 'organizationlist.view', 'organizationlist.delete', 'organizationlist.edit']
+      },
+      children: [
+        {
+          path: '',
+          component: () => import('@/views/organizationManagement/organization/organizationlist.vue'),
+          meta: {
+            title: '租户列表',
+            menu: false,
+            breadcrumb: false,
+          },
+        },
+        {
+          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.edit']
+      },
+    },
+    {
+      path: 'functionConfiguration',
+      name: 'FunctionConfigurationIndex',
+      component: () => import('@/views/organizationManagement/functionConfiguration.vue'),
+      meta: {
+        title: '功能配置',
       },
     },
-
   ],
 }
 

+ 28 - 6
src/router/modules/personnel.ts

@@ -5,21 +5,43 @@ const Layout = () => import('@/layouts/index.vue')
 const routes: RouteRecordRaw = {
   path: '/personnel',
   component: Layout,
-  redirect: '/personnel/member',
+  redirect: '/personnel/memberManagement',
   name: 'Personnel',
   meta: {
     title: '成员管理',
+    icon:'user',
   },
-  children: [
+    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',

+ 56 - 0
src/router/modules/product.ts

@@ -0,0 +1,56 @@
+import type { RouteRecordRaw } from 'vue-router'
+
+const Layout = () => import('@/layouts/index.vue')
+const routes: RouteRecordRaw = {
+  path: '/productConfiguration/drivingInsurance',
+  component: Layout,
+  redirect: '/productConfiguration/drivingInsurance/index',
+  name: 'ProductConfigurationDrivingInsurance',
+  meta: {
+    title: '产品配置',
+    icon:'zh',
+  },
+  children: [
+    {
+      path: 'index',
+      name: 'DrivingInsuranceIndex',
+      redirect: '/productConfiguration/drivingInsurance/index',
+      meta: {
+        title: '驾意险配置',
+        auth: ['drivingInsurance.add', 'drivingInsurance.view', 'drivingInsurance.delete', 'drivingInsurance.edit']
+      },
+      children: [
+        {
+          path: '',
+          component: () => import('@/views/productConfiguration/drivingInsurance/index.vue'),
+          meta: {
+            title: '驾意险配置',
+            menu: false,
+            breadcrumb: false,
+          },
+        },
+        {
+          path: 'drivingInsuranceAdd',
+          name: 'DrivingInsuranceAdd',
+          component: () => import('@/views/productConfiguration/drivingInsurance/drivingInsuranceAdd.vue'),
+          meta: {
+            title: '驾意险添加',
+            menu: false,
+            activeMenu: '/productConfiguration/drivingInsurance/index',
+          },
+        },
+      ],
+    },
+
+    {
+      path: '/productConfiguration/specialClause/index',
+      name: 'SpecialClauseIndex',
+      component: () => import('@/views/productConfiguration/specialClause/index.vue'),
+      meta: {
+        title: '特约管理',
+      },
+    },
+  ],
+}
+
+export default routes

+ 7 - 39
src/router/routes.ts

@@ -3,9 +3,10 @@ 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'
+import organizationManagement from './modules/organization'//租户管理
+import personnel from './modules/personnel'//成员管理
+import insuranceCompany from './modules/insuranceCompany'//保险公司配置
+import product from './modules/product'//产品配置
 
 // 固定路由(默认路由)
 const constantRoutes: RouteRecordRaw[] = [
@@ -61,23 +62,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
     ],
   },
@@ -93,34 +83,12 @@ const asyncRoutes: Route.recordMainRaw[] = [
   },
   {
     meta: {
-      title: '租户运营',
-      icon: 'i-uim:box',
-    },
-    children: [
-    ],
-  },
-  {
-    meta: {
-      title: '数据中心',
-      icon: 'i-uim:box',
-    },
-    children: [
-    ],
-  },
-  {
-    meta: {
-      title: '增值服务',
-      icon: 'i-uim:box',
-    },
-    children: [
-    ],
-  },
-  {
-    meta: {
-      title: '基础设置',
+      title: '设置',
       icon: 'i-uim:box',
     },
     children: [
+      insuranceCompany,
+      product
     ],
   },
 

+ 2 - 2
src/settings.ts

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

+ 4 - 6
src/store/modules/menu.ts

@@ -1,6 +1,5 @@
 import type { Menu, Route } from '#/global'
 import type { RouteRecordRaw } from 'vue-router'
-import api from '@/api'
 import menu from '@/menu'
 import { resolveRoutePath } from '@/utils'
 import { cloneDeep } from 'lodash-es'
@@ -142,8 +141,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
           }
@@ -184,9 +182,9 @@ const useMenuStore = defineStore(
     }
     // 生成导航(后端生成)
     async function generateMenusAtBack() {
-      await api.appApi.menuList().then(async (res) => {
-        filesystemMenusRaw.value = (res.data as Menu.recordMainRaw[]).filter(item => item.children.length !== 0)
-      }).catch(() => {})
+      // await api.appApi.menuList().then(async (res) => {
+      //   filesystemMenusRaw.value = (res.data as Menu.recordMainRaw[]).filter(item => item.children.length !== 0)
+      // }).catch(() => {})
     }
     // 设置主导航
     function isPathInMenus(menus: Menu.recordRaw[], path: string) {

+ 25 - 26
src/store/modules/route.ts

@@ -1,6 +1,5 @@
 import type { Route } from '#/global'
 import type { RouteRecordRaw } from 'vue-router'
-import apiApp from '@/api/modules/app'
 import { systemRoutes } from '@/router/routes'
 import { resolveRoutePath } from '@/utils'
 import { cloneDeep } from 'lodash-es'
@@ -149,33 +148,33 @@ const useRouteStore = defineStore(
       isGenerate.value = true
     }
     // 格式化后端路由数据
-    function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
-      return routes.map((route: any) => {
-        switch (route.component) {
-          case 'Layout':
-            route.component = () => import('@/layouts/index.vue')
-            break
-          default:
-            if (route.component) {
-              route.component = views[`../../views/${route.component}`]
-            }
-            else {
-              delete route.component
-            }
-        }
-        if (route.children) {
-          route.children = formatBackRoutes(route.children, views)
-        }
-        return route
-      })
-    }
+    // function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
+    //   return routes.map((route: any) => {
+    //     switch (route.component) {
+    //       case 'Layout':
+    //         route.component = () => import('@/layouts/index.vue')
+    //         break
+    //       default:
+    //         if (route.component) {
+    //           route.component = views[`../../views/${route.component}`]
+    //         }
+    //         else {
+    //           delete route.component
+    //         }
+    //     }
+    //     if (route.children) {
+    //       route.children = formatBackRoutes(route.children, views)
+    //     }
+    //     return route
+    //   })
+    // }
     // 生成路由(后端获取)
     async function generateRoutesAtBack() {
-      await apiApp.routeList().then((res) => {
-        // 设置 routes 数据
-        routesRaw.value = converDeprecatedAttribute(formatBackRoutes(res.data) as any)
-        isGenerate.value = true
-      }).catch(() => {})
+      // await apiApp.routeList().then((res) => {
+      //   // 设置 routes 数据
+      //   routesRaw.value = converDeprecatedAttribute(formatBackRoutes(res.data) as any)
+      //   isGenerate.value = true
+      // }).catch(() => {})
     }
     // 生成路由(文件系统生成)
     function generateRoutesAtFilesystem(asyncRoutes: RouteRecordRaw[]) {

+ 33 - 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,24 @@ 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){
+        getPermissions();
+        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 +67,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,
     }
   },
 )

+ 6 - 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']
@@ -16,16 +17,21 @@ declare global {
   const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
   const defineComponent: typeof import('vue')['defineComponent']
   const defineStore: typeof import('pinia')['defineStore']
+  const dictLists: typeof import('../utils/composables/dictService')['dictLists']
   const effectScope: typeof import('vue')['effectScope']
   const getActivePinia: typeof import('pinia')['getActivePinia']
   const getCurrentInstance: typeof import('vue')['getCurrentInstance']
   const getCurrentScope: typeof import('vue')['getCurrentScope']
+  const getDict: typeof import('../utils/composables/dictService')['getDict']
+  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']
   const isReactive: typeof import('vue')['isReactive']
   const isReadonly: typeof import('vue')['isReadonly']
   const isRef: typeof import('vue')['isRef']
+  const loadDicts: typeof import('../utils/composables/dictService')['loadDicts']
   const mapActions: typeof import('pinia')['mapActions']
   const mapGetters: typeof import('pinia')['mapGetters']
   const mapState: typeof import('pinia')['mapState']

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

@@ -39,6 +39,8 @@ 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']
+    Tablev2: typeof import('./../components/tablev2/index.vue')['default']
     Trend: typeof import('./../components/Trend/index.vue')['default']
   }
 }

+ 35 - 0
src/utils/composables/dictService.ts

@@ -0,0 +1,35 @@
+// dictService.ts
+import { ref } from 'vue';
+import api from '@/api';
+
+const dictLists = ref<{ [key: string]: any[] }>({});//多个字典集合
+
+// 加载多个字典
+const loadDicts = async (types: string[]): Promise<void> => {
+  const promises = types.map(async (type) => {
+    if (!dictLists.value[type + 'List']) {
+      try {
+        const res: any = await api.toolApi.dictType({ dictCode: type });
+        if (res.code == '200') {
+          dictLists.value[type + 'List'] = res.data.map((item: any) => {
+            return {
+              value: item.value,
+              label: item.label
+            };
+          });
+        }
+      } catch (error) {
+        console.error(`字典类型 ${type} 加载失败:`, error);
+      }
+    }
+  });
+
+  await Promise.all(promises);
+};
+
+// 获取字典数据
+const getDict = (type: string): any[] => {
+  return dictLists.value[type + 'List'] || [];
+};
+
+export { dictLists, loadDicts, getDict };

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

@@ -0,0 +1,23 @@
+import avatar1 from '../../assets/images/user/avatar1.png';
+import avatar2 from '../../assets/images/user/avatar2.png';
+import avatar3 from '../../assets/images/user/avatar3.png';
+import avatar4 from '../../assets/images/user/avatar4.png';
+/**
+ * 随机获取四个图片中的某一个
+ * @returns 随机选中的图片URL或文件名
+ */
+export function getRandomImage(): string {
+  // 定义四个图片的URL或文件名
+  const images: string[] = [
+    avatar1,
+    avatar2,
+    avatar3,
+    avatar4
+  ];
+
+  // 生成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();
+};

+ 56 - 10
src/utils/composables/routeUtils.ts

@@ -13,6 +13,7 @@ export interface RouteItem {
     title?: string;
     icon?: string;
     auth?: string[];
+    menu?: boolean;
   };
   children?: RouteItem[];
 }
@@ -21,7 +22,7 @@ export interface TreeNode {
   id: number;
   title: string;
   icon?: string;
-  type?:string,
+  type?: string,
   children?: TreeNode[];
   [key: string]: any; // 允许任意其他属性
 }
@@ -32,6 +33,51 @@ let idCounter = ref(0);
 const generateId = (): number => {
   return ++idCounter.value;
 };
+// function extractImportPath(func: any) {
+//   if (!func) {
+//     console.log("No function provided");
+//     return null;
+//   }
+
+//   if (typeof func !== "function") {
+//     console.error("Provided value is not a function");
+//     return null;
+//   }
+//   // 将函数转换为字符串
+//   const funcStr = func.toString();
+
+//   // 如果字符串中包含 "layouts",返回 "layout"
+//   if (funcStr.includes("layouts")) {
+//     return "layout";
+//   }
+
+//   // 检查 import() 的结构是否存在
+//   const startIndex = funcStr.indexOf('import("') + 8;
+//   const endIndex = funcStr.indexOf('")', startIndex);
+
+//   if (startIndex > 7 && endIndex > startIndex) {
+//     let path = funcStr.substring(startIndex, endIndex);
+
+//     // 如果路径中存在时间戳(例如 ?t=xxx),将其去掉
+//     if (path.includes("?")) {
+//       path = path.split("?")[0];
+//     }
+
+//     // 如果路径中存在 .vue 后缀,将其移除
+//     if (path.endsWith(".vue")) {
+//       path = path.slice(0, -4);
+//     }
+
+//     // 去掉路径中的 /src/views 前缀
+//     const prefixToRemove = "/src/views";
+//     if (path.startsWith(prefixToRemove)) {
+//       path = path.slice(prefixToRemove.length);
+//     }
+
+//     return path;
+//   }
+//   return null;
+// }
 
 // 预处理函数:确保 children 和 auth 类型正确
 export const preprocessRoutes = (routes: any[]): RouteItem[] => {
@@ -45,8 +91,8 @@ export const preprocessRoutes = (routes: any[]): RouteItem[] => {
         auth: Array.isArray(route.meta?.auth)
           ? route.meta.auth
           : route.meta?.auth
-          ? [route.meta.auth]
-          : [],
+            ? [route.meta.auth]
+            : [],
       },
       // 确保 children 存在且是数组
       children: route.children ? preprocessRoutes(route.children) : [],
@@ -65,7 +111,7 @@ const parseAuthToChildren = (authList: string[]): TreeNode[] => {
 
     return {
       id: generateId(),
-      title: `${title} (${auth})`, // 显示英文名称和原始权限
+      title: `${title}`, // 显示英文名称和原始权限
       auth: auth, // 保留原始权限路径
       type: '2',
     };
@@ -73,20 +119,20 @@ const parseAuthToChildren = (authList: string[]): TreeNode[] => {
 };
 
 // 将 RouteItem 转换为 TreeNode
-export const convertToTreeData = (id:number,routes: RouteItem[]): TreeNode[] => {
-  idCounter.value=id;
-  return routes.map(route => {
+export const convertToTreeData = (id: number, routes: RouteItem[]): TreeNode[] => {
+  idCounter.value = id;
+  return routes.filter(route => route.meta?.menu !== false).map(route => {
     const node: TreeNode = {
       id: generateId(),
       title: route.meta?.title || route.name || '',
       icon: route.meta?.icon,
-      type:route.path?"1":'0',
+      type: route.path ? "1" : '0',
+
     };
 
     if (route.children && route.children.length > 0) {
-      node.children = convertToTreeData(idCounter.value,route.children);
+      node.children = convertToTreeData(idCounter.value, route.children);
     }
-
     if (route.meta?.auth) {
       const authNodes = parseAuthToChildren(route.meta.auth);
       node.children = node.children ? node.children.concat(authNodes) : authNodes;

+ 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>
-
-<!-- 行为区域 -->
-<!-- 样式区域 -->

+ 551 - 0
src/views/insuranceCompanyConfiguration/accountOwnershipConfig.vue

@@ -0,0 +1,551 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <ElRow :gutter="20" style="height: 100%;">
+        <ElCol :md="4">
+          <div class="flex f-c " style="height: 100%;">
+            <ElFormItem>
+              <ElInput v-model="companyName" placeholder="输入保险公司名称查找" type="text" tabindex="1" clearable
+                style="width: 100%;" @clear="companyQuery">
+                <template #append>
+                  <el-button :icon="Search" @click="companyQuery" v-auth="'drivingInsurance.add'" />
+                </template>
+              </ElInput>
+            </ElFormItem>
+            <div style=" width: 100%;height: calc(100vh - 310px);  overflow-y: auto;" class="container ">
+              <div class="pad-1 roleitem " :class="{ active: companyactiveID === item.id }"
+                v-for="(item, index) in companyList" :key="index" @click="roleitemClick(item)">
+                <span>{{ item.name }}</span>
+              </div>
+            </div>
+          </div>
+        </ElCol>
+        <ElCol :md="1" style="flex: unset;">
+          <ElDivider direction="vertical" style="height: 100%;"></ElDivider>
+        </ElCol>
+        <ElCol :md="19">
+          <ElCol :md="24" :lg="24" :offset="23" v-if="companyactiveID">
+            <el-button type="primary" @click="parametervalidate(productFormRef)">保存</el-button>
+          </ElCol>
+          <ElCol :md="24" :lg="24">
+            <el-tabs v-model="editableTabsValue" type="card" editable class="demo-tabs" @edit="handleTabsEdit"
+              :closable="editableTabs.length == 1 ? false : true">
+              <el-tab-pane v-for="(item, index) in editableTabs" :key="item.name" :label="item.title" :name="item.name"
+                style=" height: 550px;overflow-y: auto;">
+                <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+                  <span class="strong">基本信息</span>
+                </ElCol>
+                <el-form :model="item" label-width="120px" :rules="parameterrules" label-position="top"
+                  ref="productFormRef" style="max-width: 1000px;" class="flex a-c f-wrap" :size="fromSize">
+                  <ElCol :md="24" :lg="12">
+                    <el-form-item label="对接方式" prop="apiType">
+                      <el-select v-model="item.apiType" placeholder="请选择对接方式" clearable filterable>
+                        <el-option v-for="(item, index) in dictLists.api_typeList" :label="item.label"
+                          :value="item.value" :key="index" />
+                      </el-select>
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="保司账号登录地址" prop="attributionConfig">
+                      <el-input v-model="item.attributionConfig" placeholder="输入账号登录地址" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="报价链接" prop="quotation">
+                      <el-input v-model="item.quotation" placeholder="输入报价链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="影像上传链接" prop="imageUpload">
+                      <el-input v-model="item.imageUpload" placeholder="输入影像上传链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="核保链接" prop="insurance">
+                      <el-input v-model="item.insurance" placeholder="输入核保链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="缴费查询链接" prop="paymentQuery">
+                      <el-input v-model="item.paymentQuery" placeholder="输入缴费查询链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="保单下载链接" prop="policyDownload">
+                      <el-input v-model="item.policyDownload" placeholder="输入保单下载链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="非车险查询链接" prop="nonCarInsurance">
+                      <el-input v-model="item.nonCarInsurance" placeholder="输入非车险查询链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="人工核保状态刷新链接" prop="manualStatusRefresh">
+                      <el-input v-model="item.manualStatusRefresh" placeholder="输入人工核保状态刷新链接" />
+                    </el-form-item>
+                  </ElCol>
+                  <ElCol :md="24" :lg="12" v-if="item.apiType == '2'">
+                    <el-form-item label="撤销订单链接" prop="revokeOrder">
+                      <el-input v-model="item.revokeOrder" placeholder="输入撤销订单链接" />
+                    </el-form-item>
+                  </ElCol>
+                </el-form>
+                <ElDivider style="margin: 0;"></ElDivider>
+                <ElCol :md="24" :lg="24" style="margin: 10px 0;font-size: 14px;">
+                  <span class="strong" style="margin-right: 10px;">基本信息</span>
+                  <el-button type="primary" @click="addParameter(index)">
+                    <template #icon>
+                      <ElIcon>
+                        <plus />
+                      </ElIcon>
+                    </template>
+                    添加参数</el-button>
+                </ElCol>
+                <el-table :data="item.fields" style="width: 100%;">
+                  <template #empty>
+                    <div class="flex f-c a-c ">
+                      <img src="../../assets/images/empty.png" alt="">
+                      <span style="font-size: 14px;color: #909399;">暂无数据</span>
+                    </div>
+                  </template>
+                  <el-table-column prop="label" label="字段标题"></el-table-column>
+                  <el-table-column prop="field" label="字段名称" width="180" align="center"></el-table-column>
+                  <el-table-column prop="type" label="类型" width="180" align="center"></el-table-column>
+                  <el-table-column prop="isEdit" label="是否必填" width="180" align="center">
+                    <template #default="scope">
+                      <span v-if="scope.row.isEdit">是</span>
+                      <span v-else>否</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column prop="value" label="默认数据" width="180" align="center"></el-table-column>
+                  <el-table-column label="操作" width="280" align="center">
+                    <template #default="scope">
+                      <el-button type="primary" link @click="fieldsDel(scope.$index, index)">删除</el-button>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-tab-pane>
+            </el-tabs>
+          </ElCol>
+
+
+        </ElCol>
+      </ElRow>
+
+    </PageMain>
+    <el-drawer v-model="fieldsAddDrawer" :direction="direction" @open="DrawerOpen">
+      <template #header>
+        <h4>编辑参数</h4>
+      </template>
+      <template #default>
+        <div>
+          <el-form :model="fieldsFrom" label-width="120px" :rules="fieldsFromrules" label-position="top"
+            ref="fieldsFromRef" :size="fromSize">
+            <el-form-item label="类型" prop="type">
+              <el-radio-group v-model="fieldsFrom.type">
+                <el-radio value="text">text</el-radio>
+                <el-radio value="select">select</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="字段标题" prop="label">
+              <el-input v-model="fieldsFrom.label" placeholder="请输入字段标题" />
+            </el-form-item>
+            <el-form-item label="字段名称" prop="field">
+              <el-input v-model="fieldsFrom.field" placeholder="请输入字段名称" />
+            </el-form-item>
+            <el-form-item label="是否必填" prop="isEdit">
+              <el-radio-group v-model="fieldsFrom.isEdit">
+                <el-radio :value="true">必填</el-radio>
+                <el-radio :value="false">非必填</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="默认数据">
+              <el-input v-model="fieldsFrom.value" placeholder="请输入默认数据" />
+            </el-form-item>
+          </el-form>
+        </div>
+      </template>
+      <template #footer>
+        <div>
+          <el-button @click="fieldsAddDrawer = false">取消</el-button>
+          <el-button type="primary" @click="fieldsAdd(fieldsFromRef)">保存</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
+</template>
+
+<script lang='ts' setup>
+import api from '@/api';
+import { ref, reactive, onBeforeMount } from 'vue';
+import { ElMessage, ComponentSize, FormInstance, TabPaneName, FormRules, DrawerProps } from 'element-plus';
+import { Search } from '@element-plus/icons-vue'
+onBeforeMount(() => {
+  companyQuery();
+  dictType('api_type');//展业范围
+
+});
+interface companyList {
+  nameSimple: string,
+  name: string,
+  id: string,
+  [key: string]: any
+}
+interface TabItem {
+  title: string;
+  name: string;
+  id?: string,
+  insCompanyId: string;
+  apiType: string;
+  attributionConfig: string;
+  quotation: string;
+  imageUpload: string;
+  insurance: string;
+  paymentQuery: string;
+  policyDownload: string;
+  nonCarInsurance: string;
+  manualStatusRefresh: string;
+  revokeOrder: string;
+  fields: FieldItem[];
+}
+
+interface FieldItem {
+  field: string;
+  label: string;
+  type: string;
+  isEdit: string;
+  value: string;
+}
+type FieldsFrom = {
+  field: string;
+  label: string;
+  type: string;
+  isEdit: string;
+  value: string;
+};
+const dictLists = ref<{ [key: string]: any[] }>({});//字典集合
+const fieldsAddDrawer = ref(false)//抽屉显示隐藏
+const direction = ref<DrawerProps['direction']>('rtl')
+const companyName = ref<string>("")//保险公司检索条件
+const companyList = ref<companyList>();
+const companyactiveID = ref("");
+const companyactiveName = ref("")
+const productFormRef = ref<FormInstance>()
+const fieldsFromRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('default')
+let tabIndex = 1//自增name标识
+const tabIdIndex = ref(0)//配置数组下标
+const editableTabsValue = ref('parameter1')
+const editableTabs = reactive<TabItem[]>([
+  {
+    title: '参数配置1',
+    name: 'parameter1',
+    id: "",
+    insCompanyId: '',//保险公司id
+    apiType: "",//对接方式
+    attributionConfig: "",//保司账号登录地址
+    quotation: "",//报价url
+    imageUpload: "",//影像url
+    insurance: "",//核保url
+    paymentQuery: "",//缴费url
+    policyDownload: "",//保单下载url
+    nonCarInsurance: "",//非车查询url
+    manualStatusRefresh: "",//人工核保刷新url
+    revokeOrder: "",//撤销订单url
+    fields: [],
+  },
+])
+const fieldsFrom: FieldsFrom = reactive({
+  field: "",
+  label: "",
+  type: "",
+  isEdit: "",
+  value: "",
+})
+const fieldsFromrules = ref<FormRules>({
+  field: [
+    { required: true, trigger: 'blur', message: '请输入字段名称' },
+  ],
+  isEdit: [
+    { required: true, trigger: 'change', message: '请选择是否必填' },
+  ],
+  label: [
+    { required: true, trigger: 'blur', message: '请输入字段标题' },
+  ],
+  type: [
+    { required: true, trigger: 'change', message: '请选择类型' },
+  ],
+})
+const parameterrules = ref<FormRules>({
+  apiType: [
+    { required: true, trigger: 'change', message: '请选择对接方式' },
+  ],
+  attributionConfig: [
+    { required: true, trigger: 'blur', message: '请输入账号登录地址' },
+  ],
+  quotation: [
+    { required: true, trigger: 'blur', message: '请输入报价链接地址' },
+  ],
+  imageUpload: [
+    { required: true, trigger: 'blur', message: '请输入影像上传链接地址' },
+  ],
+  insurance: [
+    { required: true, trigger: 'blur', message: '请输入核保链接地址' },
+  ],
+  paymentQuery: [
+    { required: true, trigger: 'blur', message: '请输入缴费查询链接地址' },
+  ],
+  policyDownload: [
+    { required: true, trigger: 'blur', message: '请输入保单下载链接地址' },
+  ],
+  nonCarInsurance: [
+    { required: true, trigger: 'blur', message: '请输入非车险查询链接地址' },
+  ],
+  manualStatusRefresh: [
+    { required: true, trigger: 'blur', message: '请输入人工核保状态刷新链接地址' },
+  ],
+  revokeOrder: [
+    { required: true, trigger: 'blur', message: '请输入撤销订单链接地址' },
+  ],
+})
+//字典信息处理
+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 query = () => {
+  api.company.attributionTemplatequery({ companyId: companyactiveID.value }).then((res: any) => {
+    if (res.code == '200' && res.data.length) {
+      // 只保留 editableTabs 中已有的字段
+      const filteredData = res.data.map((item: any) => {
+        const newItem: Partial<TabItem> = {};
+        for (const key in editableTabs[0]) {
+          if (item.hasOwnProperty(key)) {
+            newItem[key as keyof TabItem] = item[key];
+          }
+        }
+        return newItem as TabItem;
+      });
+      editableTabs.splice(0, editableTabs.length, ...filteredData);
+      editableTabsValue.value = editableTabs[0].name;
+      tabIndex = editableTabs.length;
+    } else {
+      let list: TabItem[] = [];
+      list.push({
+        title: '参数配置1',
+        name: 'parameter1',
+        id: "",
+        insCompanyId: '',//保险公司id
+        apiType: "",//对接方式
+        attributionConfig: "",//保司账号登录地址
+        quotation: "",//报价url
+        imageUpload: "",//影像url
+        insurance: "",//核保url
+        paymentQuery: "",//缴费url
+        policyDownload: "",//保单下载url
+        nonCarInsurance: "",//非车查询url
+        manualStatusRefresh: "",//人工核保刷新url
+        revokeOrder: "",//撤销订单url
+        fields: [],
+      })
+      editableTabs.length = 0; // 清空当前内容
+      editableTabs.push(...list); // 将 list 的内容添加到 editableTabs 中
+      editableTabsValue.value = editableTabs[0].name;
+      tabIndex = editableTabs.length;
+    }
+  });
+}
+const handleTabsEdit = (
+  targetName: TabPaneName | undefined,
+  action: 'remove' | 'add'
+) => {
+  if (action === 'add') {
+    const newTabName = `${++tabIndex}`
+
+    editableTabs.push({
+      title: `参数配置${newTabName}`,
+      name: 'parameter' + newTabName,
+      id: "",
+      insCompanyId: companyactiveID.value,//保险公司id
+      apiType: "",//对接方式
+      attributionConfig: "",//保司账号登录地址
+      quotation: "",//报价url
+      imageUpload: "",//影像url
+      insurance: "",//核保url
+      paymentQuery: "",//缴费url
+      policyDownload: "",//保单下载url
+      nonCarInsurance: "",//非车查询url
+      manualStatusRefresh: "",//人工核保刷新url
+      revokeOrder: "",//撤销订单url
+      fields: [],
+
+    })
+    editableTabsValue.value = 'parameter' + newTabName
+  } else if (action === 'remove') {
+    const tabs = editableTabs
+    let activeName = editableTabsValue.value
+    if (activeName != 'parameter1') {
+      if (activeName === targetName) {
+        tabs.forEach((tab, index) => {
+          if (tab.name === targetName) {
+            const nextTab = tabs[index + 1] || tabs[index - 1]
+            if (nextTab) {
+              activeName = nextTab.name
+            }
+          }
+        })
+      }
+      editableTabsValue.value = activeName
+      const index = editableTabs.findIndex((tab) => tab.name === targetName)
+      if (index !== -1) {
+        editableTabs.splice(index, 1)
+      }
+    } else {
+      ElMessage({
+        type: 'warning',
+        message: '至少保留一个',
+        plain: true,
+      })
+    }
+  }
+}
+//保存配置
+const parameterAdd = async () => {
+  editableTabs.map((ele) => {
+    ele.insCompanyId = companyactiveID.value;
+    return ele;
+  })
+  await api.company.attributionTemplate(editableTabs).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    } else {
+    }
+  });
+}
+const parametervalidate = async (formEl: FormInstance | undefined | any) => {
+  if (!formEl) return
+  const validationPromises = editableTabs.map((formRef, index) => {
+    // 轮询验证所有表单校验
+    return new Promise((resolve, reject) => {
+      if (formRef) {
+        formEl[index].validate((valid: boolean) => {
+          if (valid) {
+            resolve(true);
+          } else {
+            reject(false);
+          }
+        });
+      } else {
+        reject(false);
+      }
+    });
+  });
+
+  try {
+    await Promise.all(validationPromises);
+    // 所有表单验证通过后执行提交逻辑
+    parameterAdd();
+  } catch (error) {
+    ElMessage({
+      type: 'error',
+      message: '请确保所有表单均已填写完整',
+      plain: true,
+    });
+  }
+};
+
+//获取保险公司列表
+const companyQuery = () => {
+  api.company.selectLevelList({ name: companyName.value }).then((res: any) => {
+    if (res.code == '200') {
+      companyList.value = res.data;
+    }
+  });
+}
+//租户列表事件
+async function roleitemClick(item: { id: string, name: string }) {
+  companyactiveID.value = item.id;
+  companyactiveName.value = item.name;
+  query();
+
+}
+/**
+ *
+ * @param fieldsindex 字段数据下标
+ * @param index 配置数组下标
+ */
+const fieldsDel = (fieldsindex: any, index: number) => {
+  editableTabs[index].fields.splice(fieldsindex, 1);
+}
+// 参数添加事件
+const fieldsAdd = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      editableTabs[tabIdIndex.value].fields.push({ ...fieldsFrom })
+      fieldsAddDrawer.value = false;
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+//抽屉事件
+const addParameter = (index: number) => {
+  tabIdIndex.value = index;
+  fieldsAddDrawer.value = true
+
+}
+
+const DrawerOpen = () => {
+  //抽屉打开清空表单
+  for (const key in fieldsFrom) {
+    fieldsFrom[key as keyof FieldsFrom] = '';
+  }
+}
+</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;
+}
+
+// .container::-webkit-scrollbar {
+//   display: none;
+
+//   /* 针对 Webkit 浏览器 (如 Chrome/Safari) */
+// }
+</style>

+ 504 - 0
src/views/insuranceCompanyConfiguration/insuranceCompanyConfig.vue

@@ -0,0 +1,504 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <PageMain class="vh100">
+      <el-form :inline="true" :model="formInline" class="demo-form-inline">
+        <el-form-item label="保险公司">
+          <el-input v-model="formInline.name" placeholder="输入保险公司查找" clearable />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="companyQuery" v-auth="'insuranceCompanyConfig.view'">查询</el-button>
+        </el-form-item>
+      </el-form>
+      <div>
+        <el-button type="primary" @click="companyAdd" v-auth="'insuranceCompanyConfig.add'">
+          <template #icon>
+            <el-icon>
+              <Plus />
+            </el-icon>
+          </template>
+          添加保险公司</el-button>
+        <div :style="{ height: tableHeight, marginTop: '20px' }">
+          <el-auto-resizer>
+            <template #default="{ height, width }">
+              <el-table-v2 :columns="columns" :data="tabledata" :width="width" :height="height" v-loading="loading"
+                ref="multipleTableRef" :expand-column-key="expandColumnKey" header-class="header-class">
+                <template #cell="scope">
+                  <div v-if="scope.column.key == 'option'" class="flex j-s a-c">
+                    <el-button type="primary" link @click="addSubsidiary(scope.rowData)" v-auth="'insuranceCompanyConfig.add'"> 添加子公司
+                    </el-button>
+                    <el-dropdown placement="bottom-start">
+                      <span class="el-dropdown-link">
+                        更 多
+                      </span>
+                      <template #dropdown>
+                        <el-dropdown-menu>
+                          <Auth :value="'insuranceCompanyConfig.edit'">
+                            <el-dropdown-item @click="companyEdit(scope.rowData)">编辑</el-dropdown-item>
+                          </Auth>
+                          <Auth :value="'insuranceCompanyConfig.delete'">
+                            <el-dropdown-item @click="companyDel(scope.rowData)">删除</el-dropdown-item>
+                          </Auth>
+                        </el-dropdown-menu>
+                      </template>
+                    </el-dropdown>
+                  </div>
+                </template>
+              </el-table-v2>
+            </template>
+          </el-auto-resizer>
+        </div>
+      </div>
+    </PageMain>
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="50%" @close="close">
+      <ElRow :gutter="20">
+        <el-form :model="cpmpanyform" :inline="true" label-width="120px" :rules="cpmpanyformRules" label-position="top"
+          ref="FormRef" style="width: 100%;" :size="fromSize">
+          <ElCol :md="24" :lg="12" v-if="dialogType == 'subsidiaryadd' || dialogType == 'subsidiaryedit'">
+            <el-form-item label="上级保险公司">
+              <el-input v-model="cpmpanyform.parentName" disabled />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="保险公司名称" prop="name">
+              <el-input v-model="cpmpanyform.name" placeholder="请输入保险公司名称" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="简称" prop="nameSimple">
+              <el-input v-model="cpmpanyform.nameSimple" placeholder="请输入简称" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="dialogType == 'add' || dialogType == 'edit'">
+            <el-form-item label="类型" prop="type">
+              <el-select v-model="cpmpanyform.type" placeholder="请选择类型" style="width: 100%;" clearable filterable>
+                <el-option label="渠道" :value="0" />
+                <el-option label="财险" :value="1" />
+                <el-option label="寿险" :value="2" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="dialogType == 'add' || dialogType == 'edit'">
+            <el-form-item label="编码" prop="code">
+              <el-input v-model="cpmpanyform.code" placeholder="输入4位code编码" show-word-limit maxlength="4" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="dialogType == 'subsidiaryadd'">
+            <el-form-item label="地区">
+              <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="备注">
+              <el-input v-model="cpmpanyform.remark" placeholder="输入备注" type="textarea" maxlength="500"
+                show-word-limit />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="dialogType == 'add' || dialogType == 'edit'">
+            <el-form-item label="logo">
+              <el-upload action="#" list-type="picture-card" :auto-upload="false" :on-change="sysIconupload"
+                ref="upload1" :limit="1" :class="{ disabled: uploadDisabled }" :on-remove="handleRemove">
+                <img v-if="sysIconList.url" :src="sysIconList.url" class="avatar" style="width: 100%;height: 100%;" />
+                <el-icon v-else class="avatar-uploader-icon">
+                  <Plus />
+                </el-icon>
+              </el-upload>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="saveInfo(FormRef)">
+            保存
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='tsx' setup>
+import api from '@/api';
+import { ElTag, ElMessage } from 'element-plus'
+import ImagePreview from '@/components/ImagePreview/index.vue';
+import { FormRules, FormInstance, ComponentSize, ElMessageBox } from 'element-plus';
+
+interface Column {
+  key?: string,
+  dataKey?: string,
+  title: string,
+  width: number,
+  class?: string,
+  cellRenderer?: any
+  align?: "left" | "center" | "right"; // align 是可选的,且只能是这三个值之一
+}
+// 检索
+const formInline = reactive({
+  name: '',
+})
+//
+const cpmpanyform = reactive({
+  code: "",//编码
+  companyCode: "",
+  name: "",//名称
+  parentId: '0',
+  parentName: '',
+  nameSimple: '',//简称
+  remark: "",//备注
+  type: "",//类型
+  logo: "",//图片id
+  id: '',
+})
+const validateCode = (_rule: any, value: any, callback: any) => {
+  // 定义正则表达式,例如:只允许字母、数字,长度在4到10个字符之间
+  const codeRegex = /^[A-Z0-9]+$/i;
+  // 判断是否为空
+  if (!value) {
+    return callback(new Error('请输入code编码'));
+  }
+
+  // 验证是否符合正则表达式
+  if (!codeRegex.test(value)) {
+    return callback(new Error('code编码格式不正确,仅支持4位字母或数字'));
+  }
+
+  // 验证通过
+  callback();
+};
+const cpmpanyformRules = ref<FormRules>({
+  code: [
+    { required: true, trigger: 'blur', message: '请输入code编码' },
+    { min: 4, max: 4, message: '仅支持4位字母或数字', trigger: 'blur' },
+
+    { validator: validateCode, trigger: 'blur' }
+  ],
+  name: [
+    { required: true, trigger: 'blur', message: '请输入保险公司名称' },
+    { min: 8, max: 50, message: '请输入8-50个字符', trigger: 'blur' },
+  ],
+  nameSimple: [
+    { required: true, trigger: 'blur', message: '请输入简称' },
+  ],
+  type: [
+    { required: true, trigger: 'change', message: '请选择类型' },
+  ],
+  logo: [
+    { required: true, trigger: 'change', message: '请上传图片' },
+  ],
+});
+const sysIconList = reactive({
+  url: "",
+})
+const props = {
+  expandTrigger: 'hover' as const,
+  value: 'areaCode',
+  label: 'areaCname',
+  children: 'child',
+  checkStrictly: true,
+
+}
+const FormRef = ref<FormInstance>()
+const provinceoptions = ref()//省市区回显数据
+const areaOptions = ref()//省市区数据
+const dialogTitle = ref('添加保险公司')//弹窗title
+const dialogType = ref('add')//弹窗title  add | edit | subsidiaryadd | subsidiaryedit
+const fromSize = ref<ComponentSize>('large')//表单大小
+const loading = ref(false)
+const uploadDisabled = ref(false)//图片回显去掉上传按键
+const tableHeight = ref(''); // 初始化表格高度
+const multipleTableRef = ref();
+const tabledata = ref([]);//树形数据
+const dialogVisible = ref(false);//弹窗开关
+const expandColumnKey = 'name'//标记可展开列
+const columns: Column[] = [
+  {
+    key: 'downloadUrl', dataKey: "downloadUrl", title: "logo", width: 100, align: "center",
+    cellRenderer: ({ cellData: logo }: any) => {
+      if (logo) {
+        return <ImagePreview src={logo} width='40px' height='40px'></ImagePreview>;
+      } else {
+        return "-";
+      }
+    }
+  },
+
+  { key: 'name', dataKey: "name", title: "保险公司名称", width: 500, },
+  { key: 'companyCode', dataKey: "companyCode", title: "保险公司代码", width: 200, align: "center" },
+  { key: 'nameSimple', dataKey: "nameSimple", title: "简称", width: 200, align: "center" },
+  {
+    key: 'type', dataKey: "type", title: "类型", width: 200, align: "center", cellRenderer: ({ cellData: type }: any) => {
+      if (type == 1) {
+        return <ElTag>财险</ElTag>;
+      } else if (type == 0) {
+        return <ElTag>渠道</ElTag>;
+      } else {
+        return <ElTag>{type}</ElTag>;
+
+      }
+    }
+  },
+  {
+    key: 'option',
+    title: "操作",
+    width: 250,
+    align: 'center',
+  },
+]
+
+onMounted(() => {
+  setTimeout(() => {
+    const tableElement = multipleTableRef.value?.$el as HTMLElement;
+    if (tableElement) {
+      const offsetTop = tableElement.offsetTop; // 获取 offsetTop
+      let height = window.innerHeight;
+      tableHeight.value = height - offsetTop - 270 + "px";
+    }
+  }, 10);
+})
+onBeforeMount(() => {
+  findTree();//保险公司树结构查询
+  queryTreeQuery();//获取区划数据
+})
+const findTree = () => {
+  loading.value = true;
+  api.company.findTree({ name: formInline.name }).then((res: any) => {
+    if (res.code == '200') {
+      tabledata.value = res.data;
+      loading.value = false;
+
+    } else {
+      ElMessage({
+        type: 'error',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const companyQuery = () => {
+  findTree();
+}
+//
+const companyAdd = () => {
+  dialogVisible.value = true;
+  dialogTitle.value = '添加保险公司';
+  dialogType.value = 'add';
+}
+//添加子公司
+const addSubsidiary = (column: any) => {
+  dialogTitle.value = '添加子公司';
+  dialogType.value = 'subsidiaryadd';
+  dialogVisible.value = true;
+  cpmpanyform.parentName = column.name;
+  cpmpanyform.parentId = column.id;
+  cpmpanyform.code = column.code;
+
+}
+//删除
+const companyDel = (column: any) => {
+  ElMessageBox.confirm(
+    '保险公司删除后不可恢复,请谨慎操作',
+    '确定删除保险公司?',
+    {
+      confirmButtonText: '确定删除',
+      type: 'warning',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.company.CompanyDel(column.id).then((res: any) => {
+        if (res.code == '200') {
+          findTree();
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        } else {
+        }
+      });
+    })
+    .catch(() => {
+    })
+
+}
+// 编辑
+const companyEdit = (column: any) => {
+  if (column.parentId == '0') {
+    dialogType.value = 'edit';
+    dialogTitle.value = '编辑';
+    dialogVisible.value = true;
+    cpmpanyform.id = column.id;
+    cpmpanyform.code = column.code;
+    cpmpanyform.companyCode = column.companyCode;
+    cpmpanyform.name = column.name;
+    cpmpanyform.parentId = column.parentId;
+    cpmpanyform.nameSimple = column.nameSimple;
+    cpmpanyform.remark = column.remark;
+    cpmpanyform.type = column.type;
+    cpmpanyform.logo = column.logo;
+    sysIconList.url = column.downloadUrl;
+  } else {
+    dialogType.value = 'subsidiaryedit';
+    dialogTitle.value = '编辑';
+    dialogVisible.value = true;
+    cpmpanyform.id = column.id;
+    cpmpanyform.code = column.code;
+    cpmpanyform.companyCode = column.companyCode;
+    cpmpanyform.name = column.name;
+    cpmpanyform.nameSimple = column.nameSimple;
+    cpmpanyform.parentId = column.parentId;
+    cpmpanyform.parentName = column.parentName;
+    cpmpanyform.remark = column.remark;
+    sysIconList.url = column.downloadUrl;
+  }
+
+  // (Object.keys(cpmpanyform) as (keyof typeof cpmpanyform)[]).forEach((key) => {
+  //   if (key in column) {
+  //     cpmpanyform[key] = column[key] as any;
+  //   }
+  // });
+
+
+}
+//获取区划数据
+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 provinceChange = (value: any) => {
+  cpmpanyform.companyCode = cpmpanyform.code + '1' + value[value.length - 1];
+}
+const saveInfo = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (dialogType.value == 'add') {
+        cpmpanyform.companyCode = cpmpanyform.code + '1000000';
+        api.company.saveInsCompany({ ...cpmpanyform }).then((res: any) => {
+          if (res.code == '200') {
+            dialogVisible.value = false;
+            findTree();
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+          }
+        });
+      } else if (dialogType.value == 'subsidiaryadd') {
+        api.company.saveInsCompany({ ...cpmpanyform }).then((res: any) => {
+          if (res.code == '200') {
+            dialogVisible.value = false;
+            findTree();
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+          }
+        });
+      } else {
+        api.company.editInsCompany({ ...cpmpanyform }).then((res: any) => {
+          if (res.code == '200') {
+            dialogVisible.value = false;
+            findTree();
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+          }
+        });
+      }
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: 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) {
+      cpmpanyform.logo = res.data.id;
+      uploadDisabled.value = true;
+    }
+  })
+}
+const handleRemove = () => {
+  sysIconList.url = '';
+  uploadDisabled.value = false;
+
+}
+const close = () => {
+  cpmpanyform.code = '';
+  cpmpanyform.name = '';
+  cpmpanyform.parentId = '0';
+  cpmpanyform.parentName = '';
+  cpmpanyform.nameSimple = '';
+  cpmpanyform.remark = '';
+  cpmpanyform.type = '';
+  cpmpanyform.logo = '';
+  provinceoptions.value = [];
+  sysIconList.url = '';
+}
+</script>
+<style scoped lang="scss">
+:deep(.header-class .el-table-v2__header-cell) {
+  padding: 10px 20px;
+  font-size: 15px;
+  color: #333;
+  background-color: #eef1f6;
+  border-bottom: 1px solid #eef1f6;
+}
+
+:deep(.el-table-v2__header-row) {
+  justify-content: space-between;
+  background-color: #eef1f6;
+}
+
+:deep(.el-table-v2__row) {
+  justify-content: space-between;
+}
+
+:deep(.el-table-v2__expand-icon) {
+  margin: 0;
+}
+
+:deep(.el-table-v2__cell-text) {
+  margin-left: 10px;
+}
+
+:deep(.el-dropdown-link) {
+  display: flex;
+  align-items: center;
+  margin-left: 15px;
+  color: var(--el-color-primary);
+  cursor: pointer;
+}
+
+.disabled :deep(.el-upload--picture-card) {
+  opacity: 0;
+}
+</style>
+<!-- 样式区域 -->

+ 324 - 0
src/views/insuranceCompanyConfiguration/paymentCodeConfig.vue

@@ -0,0 +1,324 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <ElForm :inline="false" :model="formInline" class="demo-form-inline" label-width="100px">
+        <ElRow :gutter="20">
+          <ElCol :md="6">
+            <ElFormItem label="保险公司">
+              <el-input v-model="formInline.companyName" placeholder="输入保险公司查找" clearable class="el-inputs" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="4">
+            <el-button type="primary" @click="findpage">查询</el-button>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+      <ElDivider border-style="dashed"></ElDivider>
+      <Table :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200" :showSelect="false"
+        :pageInfo="pageInfo" @getList="getList">
+        <template v-slot:table-title-btn>
+          <el-button type="primary" @click="companyAdd">
+            添加保险公司
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
+        </template>
+        <template v-slot:operation="scope">
+          <el-button type="primary" link @click="paymentEdit(scope.operationData.id)"> 编辑
+          </el-button>
+          <el-button type="primary" link @click="paymentDel(scope.operationData.id)"> 删除
+          </el-button>
+        </template>
+        <template v-slot:search="scope">
+          {{ scope.slotData.label }}
+        </template>
+      </Table>
+    </PageMain>
+
+    <el-dialog v-model="paynemtAdddialogVisible" :title="dialogTitle" width="40%" @close="close">
+      <ElRow :gutter="20">
+        <el-form :model="paymentform" :inline="true" label-width="120px" :rules="memberformRules" label-position="top"
+          ref="paymentFormRef" style="width: 100%;" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="保险公司" prop="companyId">
+              <el-select v-model="paymentform.companyId" @change="companyIdChange" placeholder="请选择"
+                style="width: 100%;">
+                <el-option :label="item.name" :value="item.id" v-for="(item, index) in companyList" :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="简称" prop="companyName">
+              <el-input v-model="paymentform.companyName" placeholder="请输入姓名" disabled />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="二维码类型" prop="type">
+              <el-select v-model="paymentform.type" placeholder="请选择" style="width: 100%;">
+                <el-option v-for="(item, index) in dictLists.qr_barcodeList" :label="item.label" :value="item.value"
+                  :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="请求类型" prop="apiType">
+              <el-select v-model="paymentform.apiType" placeholder="请选择" style="width: 100%;">
+                <el-option v-for="(item, index) in dictLists.api_typeList" :label="item.label" :value="item.value"
+                  :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="paynemtAdddialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="MemberAdd(paymentFormRef)">
+            保存
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import api from '@/api';
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { FormRules, FormInstance, ComponentSize } from 'element-plus';
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+interface paymentform {
+  id?: string,
+  companyId: string,
+  companyName: string,
+  type: string,
+  apiType: string,
+  companyCode: string,
+}
+
+const tableData = ref([]);//列表数据
+const paymentFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')//表单大小
+const paynemtAdddialogVisible = ref(false)//弹窗
+const dictLists = ref<{ [key: string]: any[] }>({});//字典集合
+const dialogTitle = ref('支付码配置')//弹窗title
+const dialogType = ref('add')//弹窗title
+const companyList = ref();
+
+const paymentform = reactive<paymentform>({
+  id: "",
+  companyId: "",
+  companyName: "",
+  type: "",
+  apiType: "",
+  companyCode: "",
+})
+const memberformRules = ref<FormRules>({
+  companyId: [
+    { required: true, trigger: 'change', message: '请选择保险公司' },
+  ],
+  type: [
+    { required: true, trigger: 'change', message: '请选择二维码类型' },
+  ],
+  apiType: [
+    { required: true, trigger: 'change', message: '请选择请求类型' },
+  ],
+
+});
+//分页
+const pageInfo = ref({
+  pageNum: 1,
+  pageSize: 20,
+  sizes: [10, 20, 30, 40, 50],
+  total: 0,
+});
+//检索条件
+const formInline = reactive({
+  companyName: '',//保险公司name
+})
+onMounted(() => {
+  findpage();
+  companyQuery();//获取保险公司
+  dictType('api_type');//qpi类型
+  dictType('qr_barcode');//请求类型
+
+});
+function findpage() {
+  api.company.QRcodePage({ pages: 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 columns = [
+  { prop: "companyName", label: "保险公司", align: 'left' },
+  { prop: "companyCode", label: "保险公司代码", width: '220' },
+  {
+    prop: "type", label: "二维码类型", width: '280', formatter: (row: any) => {
+      let type = row.type;
+      let name = dictLists.value.qr_barcodeList?.find(val => val.value == type).label;
+      return name;
+    }
+  },
+  {
+    prop: "apiType", label: "请求方式", width: '280', formatter: (row: any) => {
+      let apiType = row.apiType;
+      let name = dictLists.value.api_typeList?.find(val => val.value == apiType).label;
+      return name;
+    }
+  },
+];
+//分页事件
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
+};
+//保险公司选择
+const companyIdChange = (value: string) => {
+  let Info = companyList.value.find((val: any) => val.id == value);
+  paymentform.companyName = Info.name;
+  paymentform.companyCode = Info.companyCode;
+}
+//获取保险公司列表
+const companyQuery = () => {
+  api.company.selectLevelList({ name: "" }).then((res: any) => {
+    if (res.code == '200') {
+      companyList.value = res.data;
+    }
+  });
+}
+const companyAdd = () => {
+  dialogTitle.value = '支付码配置';
+  dialogType.value = 'add';
+  paynemtAdddialogVisible.value = true
+}
+const MemberAdd = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (dialogType.value == 'add') {
+        api.company.QRBarcodeAdd({ ...paymentform }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            paynemtAdddialogVisible.value = false;
+            findpage();
+          } else {
+          }
+        });
+      } else {
+        api.company.QRBarcodeUpdate({ ...paymentform }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            paynemtAdddialogVisible.value = false;
+            findpage();
+          } else {
+          }
+        });
+      }
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+// 编辑
+const paymentEdit = (id: string) => {
+  dialogTitle.value = '支付码编辑';
+  dialogType.value = 'edit';
+  api.company.QRcodeById(id).then((res: any) => {
+    if (res.code == '200') {
+      Object.keys(paymentform).forEach((key) => {
+        if (res.data.hasOwnProperty(key)) {
+          paymentform[key as keyof typeof paymentform] = res.data[key as keyof typeof paymentform];
+        }
+      });
+      paynemtAdddialogVisible.value = true;
+    } else {
+    }
+  });
+}
+// 删除
+const paymentDel = (id: string) => {
+  ElMessageBox.confirm(
+    '确定删除吗?',
+    '删除二维码配置',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.company.QRcodeDel(id).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          findpage();
+        } else {
+        }
+      });
+    })
+    .catch(() => {
+    })
+
+}
+//字典信息处理
+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 close = () => {
+  paymentform.companyId = "";
+  paymentform.companyName = "";
+  paymentform.type = "";
+  paymentform.apiType = "";
+}
+</script>
+<style lang="scss" scoped>
+:deep(.el-divider--horizontal) {
+  margin: 8px 0 24px;
+  color: #f2f2f2;
+}
+
+.el-inputs {
+  width: 100% !important;
+}
+</style>

+ 343 - 0
src/views/organizationManagement/functionConfiguration.vue

@@ -0,0 +1,343 @@
+<!-- 模板区域 -->
+<template>
+  <div>
+    <PageMain class="vh100">
+      <el-row :gutter="20" class="vh100">
+        <el-col :span="5" class="vh100">
+          <el-input v-model="filterText" prefix-icon="search"></el-input>
+          <div class="company">
+            <div class="companyItem" :class="{'active': filterIndex === index}" v-for="(item, index) in filterChange" @click="handleClick(item, index)">
+              {{ item?.nameSimple }}
+            </div>
+          </div>
+        </el-col>
+        <el-divider direction="vertical" class="vh100"></el-divider>
+        <el-col :span="18" class="vh100">
+          <el-row :gutter="20">
+            <el-col :span="20">
+              <h4>租户功能配置</h4>
+              <p>配置租户的系统功能,报价功能,订单功能。</p>
+            </el-col>
+            <el-col :span="4">
+              <el-button type="primary" @click="handleEdit">编辑</el-button>
+            </el-col>
+          </el-row>
+          <el-divider style="width: 100%"></el-divider>
+          <el-descriptions title="系统信息配置">
+            <el-descriptions-item>
+              <el-form :model="functionForm" :rules="functionRules" ref="functionRef" label-width="200" label-position="left">
+                <el-form-item label="支持报价的保司" prop="quoteCompany">
+                  <el-radio-group style="width: 100%" v-model="functionForm.quoteCompany">
+                    <el-row :gutter="20">
+                      <el-col :span="24">
+                        <el-radio :value="1">全部保险公司</el-radio>
+                      </el-col>
+                      <el-col :span="24" style="display: flex; align-items: center">
+                        <el-radio :value="2">部分保险公司</el-radio>
+                        <el-button v-if="functionForm.quoteCompany == 2" link type="primary" @click="select" icon="CirclePlus">选择保险公司</el-button>
+                      </el-col>
+                    </el-row>
+                  </el-radio-group>
+                  <div v-if="functionForm.quoteCompany == 2" style="display: flex; justify-content: flex-start">
+                    <div style="display: flex; align-items: center; background: #E9F4FF; padding: 3px 16px; border-radius: 3px; margin-right: 10px; color: #0083FF;" v-for="(item, index) in companySelected">{{ item.nameSimple }}
+                    <el-icon style="cursor: pointer; margin-left: 20px;" @click="handleDelete(item, index)"><Close /></el-icon></div>
+                  </div>
+                </el-form-item>
+                <el-form-item label="报价/出单方式" prop="orderMode">
+                  <el-radio-group v-model="functionForm.orderMode">
+                    <el-row :gutter="20">
+                      <el-col :span="24">
+                        <el-radio :value="1">协议报价/出单</el-radio>
+                      </el-col>
+                      <el-col :span="24">
+                        <el-radio :value="2">账号报价/出单</el-radio>
+                      </el-col>
+                    </el-row>
+                  </el-radio-group>
+                </el-form-item>
+                <el-form-item label="订单审核机构" prop="orderAuditDept">
+                  <el-radio-group style="width: 100%" v-model="functionForm.orderAuditDept">
+                    <el-row :gutter="20">
+                      <el-col :span="24">
+                        <el-radio :value="1">业务员归属机构</el-radio>
+                      </el-col>
+                      <el-col :span="24">
+                        <el-radio :value="2">协议/账号归属机构</el-radio>
+                      </el-col>
+                      <el-col :span="24">
+                        <el-radio :value="3">指定机构</el-radio>
+                      </el-col>
+                    </el-row>
+                  </el-radio-group>
+<!--                  <el-cascader v-if="functionForm.orderAuditDept == 3" v-model="functionForm.orderAuditDeptList" :props="optionProps"></el-cascader>-->
+                  <div>
+                    <el-select style="width: 200px" v-if="functionForm.orderAuditDept == 3" v-model="functionForm.orderAuditDeptList">
+                      <el-option v-for="item in institutionList" :value="item.id" :label="item.name"></el-option>
+                    </el-select>
+                  </div>
+                </el-form-item>
+                <el-form-item label="业务员审核机构" prop="salesmanAuditDept">
+                  <el-radio-group style="width: 100%" v-model="functionForm.salesmanAuditDept">
+                    <el-row :gutter="20">
+                      <el-col :span="24">
+                        <el-radio :value="1">业务员归属机构</el-radio>
+                      </el-col>
+                      <el-col :span="24">
+                        <el-radio :value="2">指定机构</el-radio>
+                      </el-col>
+                    </el-row>
+                  </el-radio-group>
+<!--                  <el-cascader v-if="functionForm.salesmanAuditDept == 2" v-model="functionForm.salesmanAuditDeptList" :props="optionProps"></el-cascader>-->
+                  <el-select style="width: 200px" v-if="functionForm.salesmanAuditDept == 2" v-model="functionForm.salesmanAuditDeptList">
+                    <el-option v-for="item in institutionList" :value="item.id" :label="item.name"></el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="业务员提现账号审核机构" prop="salesmanWithdrawAccountAuditDept">
+                  <el-radio-group style="width: 100%" v-model="functionForm.salesmanWithdrawAccountAuditDept">
+                    <el-row :gutter="20">
+                      <el-col :span="24">
+                        <el-radio :value="1">业务员归属机构</el-radio>
+                      </el-col>
+                      <el-col :span="24">
+                        <el-radio :value="2">指定机构</el-radio>
+                      </el-col>
+                    </el-row>
+                  </el-radio-group>
+<!--                  <el-cascader v-if="functionForm.salesmanWithdrawAccountAuditDept == 2" v-model="functionForm.salesmanWithdrawAccountAuditDeptList" :props="optionProps"></el-cascader>-->
+                  <el-select style="width: 200px" v-if="functionForm.salesmanWithdrawAccountAuditDept == 2" v-model="functionForm.salesmanWithdrawAccountAuditDeptList">
+                    <el-option v-for="item in institutionList" :value="item.id" :label="item.name"></el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="业务员提现申请付款审核机构" prop="salesmanWithdrawPaymentAuditDept">
+                  <el-radio-group style="width: 100%" v-model="functionForm.salesmanWithdrawPaymentAuditDept">
+                    <el-row :gutter="20">
+                      <el-col :span="24">
+                        <el-radio :value="1">业务员归属机构</el-radio>
+                      </el-col>
+                      <el-col :span="24">
+                        <el-radio :value="2">指定机构</el-radio>
+                      </el-col>
+                    </el-row>
+                  </el-radio-group>
+<!--                  <el-cascader v-if="functionForm.salesmanWithdrawPaymentAuditDept == 2" v-model="functionForm.salesmanWithdrawPaymentAuditDeptList" :props="optionProps"></el-cascader>-->
+                  <el-select style="width: 200px" v-if="functionForm.salesmanWithdrawPaymentAuditDept == 2" v-model="functionForm.salesmanWithdrawPaymentAuditDeptList">
+                    <el-option v-for="item in institutionList" :value="item.id" :label="item.name"></el-option>
+                  </el-select>
+                </el-form-item>
+              </el-form>
+            </el-descriptions-item>
+          </el-descriptions>
+        </el-col>
+      </el-row>
+    </PageMain>
+    <div class="footer">
+      <el-button plain type="primary" @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleComfirm">保存</el-button>
+    </div>
+    <el-dialog
+      v-model="companyShow"
+      title="选择保险公司"
+      width="600"
+    >
+      <companys ref="companyRef" @close="handleClose"></companys>
+    </el-dialog>
+  </div>
+</template>
+
+<!-- 行为区域 -->
+<script lang='ts' setup>
+import { FormInstance } from 'element-plus'
+import { ElMessage } from 'element-plus'
+import api from '@/api'
+import companys from './modules/companySelect.vue'
+
+const functionRef = ref<FormInstance>()
+const companyRef = ref(null)
+
+// 保司相关数据
+// 保司查询条件
+let filterText = ref('')
+let company = ref([])
+let filterIndex = ref(0)
+let companyId = ref('')
+const filterChange = computed(() => {
+  return company.value.filter(a => a.nameSimple.includes(filterText.value))
+})
+const handleClick = (item, index) => {
+  filterIndex.value = index
+  companyId.value = item.id
+  console.log(11111, index)
+  handleCancel()
+}
+// 保司
+const initCompany = () => {
+  api.configurate.liveCompanyList().then(res => {
+    if(res.code == 200) {
+      company.value = res.data
+    } else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+
+// 机构
+let institutionList = ref([])
+const initInstitution = () => {
+  let code = localStorage.getItem('login_system')
+  api.configurate.getDeptListBySystemCode(code).then(res => {
+    if(res.code == 200) {
+      institutionList.value = res.data
+    } else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+
+const handleEdit = () => {
+  return api.configurate.getBusPerms({
+    systemCode: localStorage.getItem('login_system'),
+    companyId: companyId.value?companyId.value:filterChange.value[0].id
+  }).then(res => {
+    if(res.code == 200) {
+      functionForm.value = res.data
+      functionForm.value.orderAuditDeptList = functionForm.value.orderAuditDeptList[0]
+      functionForm.value.salesmanAuditDeptList = functionForm.value.salesmanAuditDeptList[0]
+      functionForm.value.salesmanWithdrawAccountAuditDeptList = functionForm.value.salesmanWithdrawAccountAuditDeptList[0]
+      functionForm.value.salesmanWithdrawPaymentAuditDeptList = functionForm.value.salesmanWithdrawPaymentAuditDeptList[0]
+      if(functionForm.value.quoteCompanyList.length > 0) {
+        companySelected.value = functionForm.value.quoteCompanyList.map(a => {
+          let obj = company.value.find(b => b.id === a)
+          return obj || null
+        })
+      }
+    } else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+
+// form表单
+let functionForm = ref({
+  companyId: '',
+  systemCode: '',
+  quoteCompany: null,
+  quoteCompanyList: [],
+  orderMode: null,
+  orderAuditDept: null,
+  orderAuditDeptList: '',
+  salesmanAuditDept: null,
+  salesmanAuditDeptList: '',
+  salesmanWithdrawAccountAuditDept: null,
+  salesmanWithdrawAccountAuditDeptList: '',
+  salesmanWithdrawPaymentAuditDept: null,
+  salesmanWithdrawPaymentAuditDeptList: ''
+})
+const functionRules = {
+  a: [
+    {  }
+  ]
+}
+const optionProps = {
+  value: 'areaCode',
+  label: 'areaCname',
+  children: 'children'
+}
+
+// 选择保险公司弹框
+let companyShow = ref(false)
+let companySelected = ref([])
+const select = () => {
+  companyShow.value = true
+  nextTick(() => {
+    companyRef.value.initEditData(companySelected.value)
+  })
+}
+const handleDelete = (item, index) => {
+  companySelected.value.splice(index, 1)
+}
+const handleClose = (data) => {
+  companyShow.value = false
+  if(data && data.length > 0) {
+    companySelected.value = JSON.parse(JSON.stringify(data))
+  }
+}
+
+//取消
+const handleCancel = () => {
+  functionForm.value = {
+    companyId: '',
+    systemCode: '',
+    quoteCompany: null,
+    quoteCompanyList: [],
+    orderMode: null,
+    orderAuditDept: null,
+    orderAuditDeptList: '',
+    salesmanAuditDept: null,
+    salesmanAuditDeptList: '',
+    salesmanWithdrawAccountAuditDept: null,
+    salesmanWithdrawAccountAuditDeptList: '',
+    salesmanWithdrawPaymentAuditDept: null,
+    salesmanWithdrawPaymentAuditDeptList: ''
+  }
+}
+
+// 保存
+const handleComfirm = () => {
+  let formData = {...functionForm.value}
+  formData.companyId = companyId.value?companyId.value:filterChange.value[0].id
+  formData.systemCode = localStorage.getItem('login_system')
+  if(companySelected.value.length > 0) {
+    formData.quoteCompanyList = companySelected.value.map(a => {
+      return a.id
+    })
+  } else {
+    formData.quoteCompanyList = []
+  }
+  formData.orderAuditDeptList = formData.orderAuditDeptList?[formData.orderAuditDeptList]:[]
+  formData.salesmanAuditDeptList = formData.salesmanAuditDeptList?[formData.salesmanAuditDeptList]:[]
+  formData.salesmanWithdrawAccountAuditDeptList = formData.salesmanWithdrawAccountAuditDeptList?[formData.salesmanWithdrawAccountAuditDeptList]:[]
+  formData.salesmanWithdrawPaymentAuditDeptList = formData.salesmanWithdrawPaymentAuditDeptList?[formData.salesmanWithdrawPaymentAuditDeptList]:[]
+  api.configurate.saveBusPerms({...formData}).then(res => {
+    if(res.code == 200) {
+      ElMessage.success(res.msg)
+    } else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+
+onMounted(() => {
+  initCompany()
+  initInstitution()
+})
+
+</script>
+<!-- 样式区域 -->
+<style scoped lang="scss">
+  .company{
+    height: calc(100% - 60px);
+    .companyItem{
+      width: 100%;
+      padding: 10px 15px;
+      margin: 10px 0;
+      overflow: hidden;
+      white-space: nowrap;
+      text-overflow: ellipsis;
+      cursor: pointer;
+      &:hover{
+        background: #F4FAFF;
+        border-radius: 3px 0 0 3px;
+      }
+    }
+    .active{
+      background: #F4FAFF;
+      border-radius: 3px 0 0 3px;
+      border-right: 1px solid #0083FF;
+    }
+  }
+  .footer{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+</style>

+ 135 - 0
src/views/organizationManagement/modules/companySelect.vue

@@ -0,0 +1,135 @@
+<template>
+  <div class="companySelect">
+    <el-row class="content" :gutter="20">
+      <el-col :span="11">
+        <el-input v-model="searchName" placeholder="输入驾意险名称查找" style="margin-bottom: 10px">
+          <template #append>
+            <el-button type="primary" @click="handleSearchName">查找</el-button>
+          </template>
+        </el-input>
+        <div>
+          <el-checkbox v-model="selectAll" @change="handleSelectAll">全选</el-checkbox>
+          <el-checkbox-group v-model="selected" @change="handleSelect">
+            <el-checkbox v-for="item in selectList" :value="item.id" :label="item.nameSimple"></el-checkbox>
+          </el-checkbox-group>
+        </div>
+      </el-col>
+      <el-divider direction="vertical" style="height: 100%"></el-divider>
+      <el-col :span="12">
+        <div style="display: flex; align-items: center; justify-content: space-between; margin: 8px 0">
+          <span>已选: {{ selectedList.length }}家保险公司</span>
+          <el-button link type="primary">清空</el-button>
+        </div>
+        <div class="selectedList">
+          <div style="display: flex; align-items: center; justify-content: space-between; margin: 10px" v-for="(item, index) in selectedList" :key="item.id">
+            <span>{{ item.nameSimple }}</span>
+            <el-icon style="cursor: pointer;" @click="handleDel(item, index)"><Close /></el-icon>
+          </div>
+        </div>
+      </el-col>
+    </el-row>
+    <el-divider style="width: 100%"></el-divider>
+    <div class="footer">
+      <el-button plain @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleConfirm">保存</el-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {ElMessage} from "element-plus";
+
+const emits = defineEmits(['close'])
+import api from '@/api'
+
+let company = ref([]) // 保险公司
+
+// 勾选保险公司
+let selectAll = ref(false)
+let selected = ref([])
+let selectList = ref([])
+let searchName = ref('')
+let selectedList = ref([])
+
+const handleSelectAll = (e) => {
+  if(e) {
+    selectedList.value = JSON.parse(JSON.stringify(selectList.value))
+    selected.value = selectList.value.map(a => {
+      return a.id
+    })
+  } else {
+    selectedList.value = []
+    selected.value = []
+  }
+}
+
+const handleSearchName = () => {
+  let list = company.value.filter(a => a.nameSimple.includes(searchName.value))
+  selectList.value = [...list]
+}
+
+const handleSelect = (e) => {
+  if(e.length > 0) {
+    selectedList.value = e.map(a => {
+      let obj = selectList.value.find(b => b.id === a)
+      return obj || null
+    })
+  }
+}
+
+const handleDel = (item, index) => {
+  selectedList.value.splice(index, 1)
+  if(selectedList.value.length > 0) {
+    selected.value = company.value.map(a => {
+      let obj = selectedList.value.find(b => b.id === a.id)
+      return obj?.id
+    })
+  } else {
+    selected.value = []
+    selectAll.value = false
+  }
+}
+
+const initEditData = (list) => {
+  api.configurate.liveCompanyList().then(res => {
+    if(res.code == 200) {
+      company.value = JSON.parse(JSON.stringify(res.data))
+      selectList.value = JSON.parse(JSON.stringify(res.data))
+      if(list && list.length > 0) {
+        selectedList.value = JSON.parse(JSON.stringify(list))
+        selected.value = selectedList.value.map(a => {
+          return a.id
+        })
+      }
+    } else {
+      ElMessage.error(res.msg)
+    }
+  })
+}
+
+const handleCancel = () => {
+  emits('close', [])
+}
+const handleConfirm = () => {
+  emits('close', selectedList.value)
+}
+
+defineExpose({
+  initEditData
+})
+
+</script>
+
+<style scoped lang="scss">
+.companySelect{
+  height: 400px;
+  .content{
+    height: calc(100% - 80px);
+  }
+  .footer{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+</style>

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

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

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

@@ -0,0 +1,419 @@
+<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="请输入名称" maxlength="20" show-word-limit />
+              <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="请输入管理员姓名" :disabled="userNameShow" />
+              <span style="opacity: 0;">x</span>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="管理员账号" prop="userAccount">
+              <el-input style="display: none;" type="password" />
+              <el-input v-model="sysform.userAccount" placeholder="请输入登录账号" maxlength="11" show-word-limit type="text" name="formerEmployer"
+                autocomplete="new-password" @blur="userAccountblur" />
+              <span style="font-size: 0.7rem;color: #999;">管理员手机号将作为登录账号使用</span>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="!id">
+            <el-form-item label="登录密码" prop="password">
+              <el-input style="display: none;" type="text" />
+              <el-input v-model="sysform.password" placeholder="请输入登录密码" show-password type="password"
+                autocomplete="new-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" style="width: 100%;height: 100%;" />
+                <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" @click="router.back()">
+        取消
+      </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 } from 'element-plus';
+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()//省市区回显数据
+const areaOptions = ref()//省市区数据
+const uploadDisabled = ref(false)
+const dialogImageUrl = ref('')
+const dialogVisible = ref(false)
+const ruleFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')
+const userNameShow = ref(false)
+const props = {
+  expandTrigger: 'hover' as const,
+  value: 'areaCode',
+  label: 'areaCname',
+  children: 'child'
+}
+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 validatepassword = (_rule: any, value: any, callback: any) => {
+  // 定义正则表达式,例如:只允许字母、数字,长度在4到10个字符之间
+  const codeRegex = /^[A-Z0-9]+$/i;
+  // 判断是否为空
+  if (!value) {
+    return callback(new Error('请输入code编码'));
+  }
+
+  // 验证是否符合正则表达式
+  if (!codeRegex.test(value)) {
+    return callback(new Error('code编码格式不正确,仅支持8~20位字母或数字'));
+  }
+
+  // 验证通过
+  callback();
+};
+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: '请输入密码' },
+    { min: 8, max: 20, message: '仅支持8~20位字母或数字', trigger: 'blur' },
+    { validator: validatepassword, trigger: 'blur' }
+  ],
+
+  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(() => {
+  queryTreeQuery();//获取区划数据
+  if (id.value) {
+    detailQuery();
+  }
+});
+const userAccountblur = (event: any) => {
+  api.tenantApi.userGetPhone(event.target.value).then((res: any) => {
+    if (res.code == '200' && res.data.length) {
+      sysform.value.userName = res.data[0].username;
+      userNameShow.value = true;
+    } else {
+    }
+  });
+
+}
+//获取区划数据
+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 detailQuery = () => {
+  api.tenantApi.sysGet({ id: id.value }).then((res: any) => {
+    if (res.code == '200') {
+      sysform.value = res.data;
+      sysIconList.url = res.data.sysIconUrl
+      provinceoptions.value = [res.data.provinceId, res.data.cityId, res.data.districtId]
+
+      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 && value[0] != null && value[1] != null && value[2] != null) {
+    sysform.value.provinceId = value[0];
+    sysform.value.cityId = value[1];
+    sysform.value.districtId = value[2];
+  } else {
+    sysform.value.provinceId = "";
+    sysform.value.cityId = "";
+    sysform.value.districtId = "";
+  }
+}
+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();
+        router.back();
+
+      } else {
+        api.tenantApi.sysAdd(sysform.value).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back();
+
+          } else {
+            ElMessage({
+              type: 'error',
+              message: res.msg,
+              plain: true,
+
+            })
+          }
+        })
+      }
+    } else {
+
+    }
+  })
+}
+
+</script>
+<style lang="scss" scoped>
+.disabled :deep(.el-upload--picture-card) {
+  display: none;
+}
+
+// .disabled >>> .el-upload--picture-card {
+//   display: none;
+// }
+</style>

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

@@ -0,0 +1,458 @@
+<!-- 模板区域 -->
+<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="租户logo">
+            <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.division }}
+              </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" :columnwidth="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 { 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 columns = [
+  { prop: "name", label: "机构名称", width: '300', align: 'left' },
+  { prop: "comType", label: "机构类型", width: '120', formatter: (row: any) => { let comType = sysTypematch(row.comType); return comType } },
+  {
+    prop: "exhibitionScope", label: "营业范围", width: '200', formatter: (row: any) => {
+      let exhibitionScope = row.exhibitionScope;
+      if (exhibitionScope) {
+        let sysType = exhibitionScope.join(',');
+        return sysType;
+      }
+    }
+  },
+  { prop: "division", label: "所属地区" },
+  { prop: "leaderName", 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',
+      center: true,
+    }
+  )
+    .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',
+      center: true,
+    }
+  )
+    .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;
+        border-radius: 50%;
+      }
+
+      .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>

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

@@ -0,0 +1,421 @@
+<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="选择类型" 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" v-auth="'organizationlist.view'" @click="roleQuery">查询</el-button>
+                <el-button type="primary" v-auth="'organizationlist.view'" @click="resetForm">重置</el-button>
+              </div>
+            </ElFormItem>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+      <Table :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200" :showSelect="true"
+        :pageInfo="pageInfo" @getList="getList" @selectAllChange="handleSelectionChange"
+        @selectionChange="selectionChange">
+        <template v-slot:table-title-btn>
+          <RouterLink :to="{ name: 'OrganizationAdd' }">
+            <el-button type="primary" v-auth="'organizationlist.add'">
+              添加租户
+              <template #icon>
+                <el-icon>
+                  <Plus />
+                </el-icon>
+              </template>
+            </el-button>
+          </RouterLink>
+          <el-dropdown placement="bottom-start" v-auth="'organizationlist.delete'">
+            <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)" v-auth="'organizationlist.edit'"> 编辑
+          </el-button>
+          <el-dropdown placement="bottom-start">
+            <span class="el-dropdown-link">
+              更 多
+            </span>
+            <template #dropdown>
+              <el-dropdown-menu>
+                <Auth :value="'organizationlist.delete'">
+                  <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>
+                </Auth>
+                <Auth :value="'organizationlist.add'">
+                  <el-dropdown-item @click="subOrganizationAdd(scope.operationData.sysCode)">添加子机构</el-dropdown-item>
+                </Auth>
+              </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 { RouterLink, useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from 'element-plus'
+import useAuth from '@/utils/composables/useAuth'
+const { auth } = useAuth()
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+const props = {
+  expandTrigger: 'hover' as const,
+  value: 'areaCode',
+  label: 'areaCname',
+  children: 'child'
+}
+const sysUpAndDownList = ref<string[]>([])//选中id
+const provinceoptions = ref<string[]>([])//省市区回显数据
+const areaOptions = ref()//省市区数据
+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: '',
+  provinceId: "",
+  cityId: "",
+  districtId: "",
+})
+const tableData = ref([]);
+onBeforeMount(() => {
+  if (auth('organizationlist.view')) {
+    findpage();//列表数据
+    queryTreeQuery();//区划数据
+  }
+});
+//获取区划数据
+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,
+      })
+    }
+  });
+}
+function findpage() {
+  api.tenantApi.sysList({ pages: 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.pages;
+      pageInfo.value.pageSize = res.data.size;
+      pageInfo.value.total = res.data.total;
+    }
+  })
+}
+const columns = [
+  { prop: "sysCode", label: "编码", width: '120', },
+  {
+    prop: "sysName", label: "租户名称", width: '200', component: "RouterLink",
+    formatter: (row: any) => ({ name: 'OrganizationDetail', query: { id: row.id, code: row.sysCode, typeName: 'tenantInfo' } })
+  },
+  { prop: "sysIconUrl", label: "租户图标", width: '120', component: 'Image' },
+  {
+    prop: "sysType", label: "租户类型", width: '120', formatter: (row: any) => {
+      let sysType = '';
+      sysTypelist.value.map(ele => {
+        if (ele.id == row.sysType) {
+          sysType = ele.name;
+        }
+      })
+      return sysType
+    }
+  },
+  { prop: "division", label: "所在地区", },
+  {
+    prop: "deptCount", label: "下级机构",
+    component: "RouterLink",
+    formatter: (row: any) => ({ name: 'OrganizationDetail', query: { id: row.id, code: row.sysCode, typeName: 'organization' } })
+  },
+  {
+    prop: "available", label: "状态", width: '120', component: "Tag",
+    formatter: (row: any) => {
+      if (row.available) {
+        return { type: 'primary', text: '启用' };
+      } else {
+        return { type: 'danger', text: '禁用' };
+      }
+    }
+  },
+  { prop: "createTime", label: "创建时间", width: '180', },
+  { prop: "createBy", label: "创建人", width: '120', },
+];
+
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
+
+};
+function tenantUpdate(item: any) {
+  router.push({
+    name: 'OrganizationAdd',
+    query: {
+      id: item.id
+    }
+  })
+}
+const handleSelectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+const selectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+//删除
+const del = (value: string) => {
+  ElMessageBox.confirm(
+    '机构删除后不可恢复,请谨慎操作',
+    '确定删除机构?',
+    {
+      confirmButtonText: '确定删除',
+      cancelButtonText: '取消',
+      type: 'error',
+      center: true,
+    }
+  )
+    .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: '确定禁用',
+      type: 'warning',
+      center: true,
+    }
+  )
+    .then(() => {
+      sysUpAndDown(0, [value]);
+    })
+    .catch(() => {
+
+    })
+
+}
+//启用
+const enable = (value: string) => {
+  sysUpAndDown(1, [value]);
+}
+//批量删除
+const batchDel = () => {
+  ElMessageBox.confirm(
+    '机构删除后不可恢复,请谨慎操作',
+    '确定删除机构?',
+    {
+      confirmButtonText: '确定删除',
+      cancelButtonText: '取消',
+      type: 'error',
+      center: true,
+    }
+  )
+    .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',
+      center: true,
+    }
+  )
+    .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 = '';
+  provinceoptions.value=[];
+  findpage();
+}
+
+</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>

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

@@ -0,0 +1,290 @@
+<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>
+        </el-form>
+      </ElRow>
+
+    </PageMain>
+    <FixedActionBar>
+      <ElButton type="primary" size="large" @click="submitForm(deptFormRef)">
+        保存
+      </ElButton>
+      <ElButton size="large" @click='router.back()'>
+        取消
+      </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: "",//机构地址
+  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.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) => {
+  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>

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

@@ -0,0 +1,223 @@
+<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: roleInfo.systemCode === item.sysCode }"
+                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" v-auth="'tenantPermissions.edit'">保存</ElButton>
+            </div>
+          </div>
+          <ElDivider style="margin-top: 10px;"></ElDivider>
+          <div v-if="roleInfo.systemCode">
+            <el-checkbox v-model="treekAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">
+              全选
+            </el-checkbox>
+            <el-checkbox-group v-model="checkedCities" class="flex f-c" @change="handleCheckedCitiesChange">
+              <el-checkbox v-for="item in treeDatalevelOne" :key="item" :label="item" :value="item">
+                {{ item }}
+              </el-checkbox>
+            </el-checkbox-group>
+          </div>
+          <el-empty v-else image="../../../src/assets/images/empty.png" />
+        </ElCol>
+      </ElRow>
+
+    </PageMain>
+  </div>
+</template>
+
+<script lang='ts' setup>
+import { ref, reactive, onBeforeMount } from 'vue';
+import { ElMessage } from 'element-plus';
+import { TreeNode } from '@/utils/composables/routeUtils'; // 导入工具方法
+import { Search } from '@element-plus/icons-vue'
+import api from '@/api'
+
+onBeforeMount(() => {
+  findpage()//租户列表
+  getMenu();//获取租户菜单
+
+});
+interface tenantList {
+  sysName: string,
+  id: string,
+  [key: string]: any
+}
+const roleInfo = reactive<{
+  systemCode: string;
+  perms: object[],
+  permsStr: string,
+}>({
+  systemCode: "",//角色
+  permsStr: '',//菜单json
+  perms: [],//权限标识
+});
+const treekAll = ref(false)//全选
+const isIndeterminate = ref(true)
+const checkedCities = ref([])//默认选中
+const searchValue = ref<string>()//检索条件
+const tenantList = ref<tenantList>()//租户列表
+const treeData = ref<TreeNode[]>([]);//菜单原始数据
+const treeDatalevelOne = ref();//一级菜单数据
+const handleCheckAllChange = (val: any) => {
+  checkedCities.value = val ? treeDatalevelOne.value : []
+  isIndeterminate.value = false
+}
+const handleCheckedCitiesChange = (value: string[]) => {
+  const checkedCount = value.length
+  treekAll.value = checkedCount === treeDatalevelOne.value.length
+  isIndeterminate.value = checkedCount > 0 && checkedCount < treeDatalevelOne.value.length;
+
+}
+const treeDatalevelFun = () => {
+  let list: any = [];
+  treeData.value.map((val: any) => {
+    list.push(val.meta.title)
+  })
+  treeDatalevelOne.value = list;
+}
+const dataSetProcessing = () => {
+  let menuList: any = [];
+  treeData.value.map(value => {
+    checkedCities.value.map(val => {
+      if (value.meta.title === val) {
+        menuList.push(value)
+      }
+    })
+  })
+  roleInfo.perms = menuList;
+  if (menuList.length) {
+    roleInfo.permsStr = JSON.stringify(menuList);
+
+  }
+}
+const getMenu = () => {
+  api.tenantApi.getSystemConfig({ systemCode: 'PLATFORM' }).then((res: any) => {
+    if (res.code == '200') {
+      treeData.value = JSON.parse(res.data.sysConfig)
+      treeDatalevelFun();
+    }
+  });
+}
+// 提交
+async function roleSubmit() {
+  dataSetProcessing();
+  await api.tenantApi.updatePerms(roleInfo).then((res: any) => {
+    if (res.code == '200') {
+      ElMessage({
+        type: 'success',
+        message: "租户权限保存成功",
+        plain: true,
+      })
+      findpage()//租户列表
+    } else {
+    }
+  });
+}
+//租户列表事件
+async function roleitemClick(item: { sysCode: string }) {
+  getMenu();//获取租户菜单
+  roleInfo.systemCode = item.sysCode;
+  api.tenantApi.getTenantConfig({ systemCode: item.sysCode }).then((res: any) => {
+    if (res.code == '200') {
+      let list: any = [];
+      if (res.data.sysConfig) {
+        let tree = JSON.parse(res.data.sysConfig);
+        console.log(tree)
+        tree.map((val: any) => {
+          list.push(val.meta.title)
+        })
+        checkedCities.value = list;
+        treekAll.value = true;
+      } else {
+        checkedCities.value = [];
+        treekAll.value = false;
+
+      }
+    } else {
+    }
+  });
+}
+const findpage = () => {
+  api.tenantApi.sysListAll({  searchValue: searchValue.value }).then((res: any) => {
+    if (res.code == 200) {
+      tenantList.value = res.data;
+    }
+  })
+}
+
+
+
+</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>

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

@@ -0,0 +1,78 @@
+<!-- 模板区域 -->
+<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 } from 'vue'
+import { FormRules, ComponentSize } from 'element-plus';
+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>
+<!-- 样式区域 -->

+ 449 - 256
src/views/personnel/memberManagement.vue

@@ -1,86 +1,74 @@
 <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" v-auth="'member.view'"
+                  @keydown.enter="handleEnter">查询</el-button>
+                <el-button type="default" @click="resetForm" v-auth="'member.view'">重置</el-button>
+              </div>
+            </ElFormItem>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+      <ElDivider border-style="dashed"></ElDivider>
+      <Table :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200" :showSelect="true"
+        :pageInfo="pageInfo" @getList="getList" @selectAllChange="handleSelectionChange"
+        @selectionChange="selectionChange">
         <template v-slot:table-title-btn>
-          <el-button type="primary" @click="add(ruleFormRef)">
-              <el-icon><Plus /></el-icon>添加成员</el-button
-            >
-          <el-dropdown placement="bottom-start">
-            <el-button> 批量操作<el-icon class="el-icon--right">
+          <el-button type="primary" @click="userAdd" v-auth="'member.add'">
+            添加成员
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
+          <el-dropdown placement="bottom-start" v-auth="'member.delete'">
+            <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-dropdown placement="bottom-start">
+          <el-button type="primary" link @click="tenantUpdate(scope.operationData.id)" v-auth="'member.edit'"> 编辑
+          </el-button>
+          <el-dropdown placement="bottom-start" v-auth="'member.delete'">
             <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.isEnable == 1"
+                  @click="disable(scope.operationData.id)">禁用</el-dropdown-item>
+                <el-dropdown-item v-if="scope.operationData.isEnable == 0"
+                  @click="enable(scope.operationData.id)">启用</el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
@@ -89,231 +77,436 @@
           {{ 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%" @close="close">
+      <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="roleIdList">
+              <el-select v-model="memberform.roleIdList" 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";
 
-// import universalTable from "../"
-const formAccessibility = reactive({
-  fullName: "",
-  firstName: "",
-  lastName: "",
-  phone: "",
-});
-const ruleFormRef = ref<FormInstance>();
-const form = reactive({
-  name: "",
-  role: "",
-  password: "",
-  phone: "",
-});
+</template>
 
-interface RuleForm {
-  name: string;
-  phone: string;
-  role: string[];
-  password: string;
-}
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import api from '@/api';
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { FormRules, FormInstance, ComponentSize } from 'element-plus';
 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",
-  },
+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 memberform = reactive({
+  username: "",//机构名称
+  mobile: "",//上级机构
+  roleIdList: [],
+  id: '',
+})
+const memberformRules = ref<FormRules>({
+  username: [
+    { required: true, trigger: 'blur', message: '请输入姓名' },
+    { min: 2, max: 20, message: '请输入2~20个字符', trigger: 'blur' },
+  ],
+  roleIdList: [
+    { required: true, trigger: 'change', message: '请选择角色' },
+  ],
+  mobile: [
+    { required: true, trigger: 'blur', message: '请输入手机号' },
+  ],
+
+});
+//分页
+const pageInfo = ref({
+  pageNum: 1,
+  pageSize: 20,
+  sizes: [10, 20, 30, 40, 50],
+  total: 0,
+});
+//检索条件
+const formInline = reactive({
+  searchValue: '',
+  isEnable: '',
+})
+onMounted(() => {
+  findpage();
+  roleListfun();
+  window.addEventListener('keydown', handleEnter)
+});
+onUnmounted(() => {
+  window.removeEventListener('keydown', handleEnter, false)
+})
+function findpage() {
+  api.member.userList({ pages: 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: 'error',
+          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 columns = [
+  { prop: "username", label: "姓名", width: '120', },
+  { prop: "mobile", label: "手机号", width: '120' },
+  { prop: "id", label: "工号", width: '180', },
   {
-    value: "Option4",
-    label: "Option4",
+    prop: "roleNameList", label: "角色", formatter: (row: any) => {
+      let roleNameList = row.roleNameList;
+      if (roleNameList) {
+        let roleName = roleNameList.join(',');
+        return roleName;
+      } else {
+        return roleNameList;
+      }
+    }
   },
   {
-    value: "Option5",
-    label: "Option5",
+    prop: "isEnable", label: "状态", component: 'Tag', formatter: (row: any) => {
+      if (row.isEnable) {
+        return { type: 'primary', text: '启用' };
+      } else {
+        return { type: 'danger', text: '禁用' };
+      }
+    }
   },
 ];
-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();
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
 };
-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" },
-  ],
-  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: "状态" },
+function tenantUpdate(id: string) {
+  dialogTitle.value = '编辑成员';
+  dialogType.value = 'edit';
+  memberform.roleIdList = [];
+  let userlist: any = [];
+  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;
+      roleLists.value.map((val: any) => {
+        if (res.data.roleNameList) {
+          res.data.roleNameList.map((value: any) => {
+            if (value === val.name) {
+              userlist.push(val.id)
+            }
+          })
+          memberform.roleIdList = userlist;
+        } else {
+          memberform.roleIdList = [];
+        }
 
-  // {
-  //     prop: "operation", label: "operation", width: "280",
-  //     type:'operation', btn:[{
-  //         type:"pramary",
-  //         label:"编辑"
-  //     }]
-  // },
-];
-const pageInfo = {
-  pageNum: 1,
-  pageSize: 10,
-  sizes: [10, 20, 30, 40, 50],
-  total: 100,
-};
-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
-};
+      })
+      UpdatedialogVisible.value = true;
+    } else {
+      ElMessage({
+        type: 'success',
+        message: res.msg,
+        plain: true,
+      })
+    }
+  });
+}
+const handleSelectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
+}
+const selectionChange = (val: any) => {
+  sysUpAndDownList.value = val;
 
-const operationMany = (item: any) => {
-  // pageInfo.pageNum=item.pageNum
-  // pageInfo.pageSize=item.pageSize
-};
+}
+//删除
+const del = (value: string) => {
+  ElMessageBox.confirm(
+    '账号删除后,该账号将无法使用访问本企业,请确定是否删除?',
+    '确定要删除账号吗?',
+    {
+      confirmButtonText: '确认删除',
+      cancelButtonText: '取消',
+      type: 'error',
+      center: true,
+    }
+  )
+    .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 reset = (item: any) => {
-  // ElMessage({})
-};
+}
+
+//禁用
+const disable = (value: string) => {
+  ElMessageBox.confirm(
+    '确定要禁用账号吗?',
+    '禁用账号',
+    {
+      confirmButtonText: '确认禁用',
+      cancelButtonText: '取消',
+      type: 'error',
+      center: true,
+    }
+  )
+    .then(() => {
+      sysUpAndDown(0, [value]);
+    })
+    .catch(() => {
+
+    })
+}
+//启用
+const enable = (value: string) => {
+  sysUpAndDown(1, [value]);
+
+}
+//批量删除
+const batchDel = () => {
+  ElMessageBox.confirm(
+    '账号删除后,该账号将无法使用访问本企业,请确定是否删除?',
+    '确定要删除账号吗?',
+    {
+      confirmButtonText: '确认删除',
+      cancelButtonText: '取消',
+      type: 'error',
+      confirmButtonClass: 'el-button--danger',
+      center: true,
+    }
+  )
+    .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',
+      center: true,
+    }
+  )
+    .then(async () => {
+      await sysUpAndDown(0, undefined);
+    })
+    .catch(() => {
+
+    })
+
+
+}
+//批量启用
+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 = "";
+  findpage();
+}
+const userAdd = () => {
+  dialogTitle.value = '添加成员';
+  dialogType.value = 'add';
+  memberform.id = "";
+  UpdatedialogVisible.value = true;
+}
+const close = () => {
+  memberform.username = '';
+  memberform.mobile = '';
+  memberform.roleIdList = [];
+  memberform.id = '';
+}
 </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>

+ 117 - 76
src/views/personnel/roleManagement.vue

@@ -1,64 +1,88 @@
 <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" v-auth="'role.view'" />
+                </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" v-auth="'role.add'">
+                <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 v-if="roleInfo.id" type="primary" @click="roleSubmit" v-auth="'role.add'">编辑</ElButton>
+              <ElButton v-else type="default" @click="roleSubmit" v-auth="'role.add'">保存</ElButton>
+              <ElButton v-if="roleInfo.id" type="default" @click="roleDelClick" v-auth="'role.delete'">删除</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-if="roleInfo.id || 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>
+          <div class="flex f-c a-c " v-else>
+            <img src="../../assets/images/empty.png" alt="">
+            <span style="font-size: 14px;color: #909399;">暂无数据</span>
+          </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,16 +90,17 @@
 <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'
-
 onBeforeMount(() => {
   rolequery()//角色列表
   // 对 asyncRoutes 进行预处理并转换为树形结构
   const processedRoutes = preprocessRoutes(asyncRoutes);
   treeData.value = convertToTreeData(0, processedRoutes);
+
 });
 interface roleList {
   id: string,
@@ -89,12 +114,8 @@ interface Perm {
 const treeRef = ref<InstanceType<typeof ElTree>>();
 const roleName = ref<string>("");
 const roleId = ref<string>("");
-
-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;
@@ -176,6 +197,7 @@ function roleDelClick() {
           cancelButtonText: '取消',
           type: 'error',
           draggable: true,
+          center: true,
         }
       )
         .then(async () => {
@@ -193,23 +215,15 @@ function roleDelClick() {
 
         })
     } else {
-      ElMessageBox.alert('删除角色前,请先移除角色内的所有成员', '无法删除', {
-        confirmButtonText: '知道了',
-
-      })
     }
   })
 
 
-}
-function pageChange(e: number) {
-  PageNum.value = e;
-  PageSize.value = 20;
-}
-function sizeChange(val: number) {
-  PageSize.value = val;
 }
 async function roleitemClick(item: { id: string }) {
+  if (treeRef.value) {
+    treeRef.value.setCheckedKeys([]);
+  }
   await api.roleApi.roleGet({ roleId: item.id }).then((res: any) => {
     if (res.code === 200) {
       checkedKeys.value = res.data.perms.map((val: Perm) => val.id);
@@ -223,20 +237,21 @@ 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";
   roleInfo.permsId = [];
   roleInfo.perms = [];
   roleInfo.id = '';
+  roleId.value = '';
   if (treeRef.value) {
-    treeRef.value.setCheckedKeys([]);
+    treeRef.value.setCheckedKeys([]);//清空菜单勾选回显
   }
 }
 
@@ -245,21 +260,47 @@ function resetForm() {
 
 <style lang="scss" scoped>
 .roleitem {
-  cursor: pointer;
-  background: white;
+  span:first-child {
+    font-size: 14px;
+    color: #333;
+  }
+
+  span:last-child {
+    font-size: 12px;
+    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>

+ 494 - 0
src/views/productConfiguration/drivingInsurance/drivingInsuranceAdd.vue

@@ -0,0 +1,494 @@
+<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="productform" label-width="120px" :rules="productformRules" label-position="top"
+          ref="productFormRef" style="max-width: 1000px;" class="flex a-c f-wrap" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="产品类型" prop="productType">
+              <el-select v-model="productform.productType" :disabled="productType == '2' || type == 'edit'"
+                @change="productTypeChange" placeholder="请选择产品类型" clearable filterable>
+                <el-option label="主险" value="1" />
+                <el-option label="附加险" value="2" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="productform.productType != '1'">
+            <el-form-item label="上级产品" prop="parentId">
+              <el-select v-model="productform.parentId" :disabled="productType == '2' || type == 'edit'"
+                placeholder="请选择产品" clearable>
+                <el-option v-for="(item, index) in parentList" :key="index" :label="item.productName"
+                  :value="item.id" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="产品名称" prop="productName">
+              <el-input v-model="productform.productName" placeholder="输入产品名称" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="别名">
+              <el-input v-model="productform.alias" placeholder="输入产品别名" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="保险公司" prop="company">
+              <el-input v-model="productform.companyName" placeholder="保险公司" disabled />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="产品编码" prop="productCode">
+              <el-input v-model="productform.productCode" placeholder="点击查询,获取产品编码" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="对接方式" prop="connectionMethod">
+              <el-select v-model="productform.connectionMethod" placeholder="请选择产品" clearable filterable>
+                <el-option label="API对接" value="1" />
+                <el-option label="Python对接" value="2" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="保费设置" prop="premiumSetting">
+              <el-select v-model="productform.premiumSetting" placeholder="请选择" clearable filterable>
+                <el-option label="产品统一价格" value="1" />
+                <el-option label="按座位数设置价格" value="2" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="productform.premiumSetting == '1'">
+            <el-form-item label="总保额" prop="coverage">
+              <el-input v-model="productform.coverage" placeholder="请输入总保额">
+                <template #append>元</template>
+              </el-input>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12" v-if="productform.premiumSetting == '1'">
+            <el-form-item label="总保费" prop="premium">
+              <el-input v-model="productform.premium" placeholder="请输入总保费">
+                <template #append>元</template>
+              </el-input>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+        <ElCol :md="24" :lg="24" v-if="productform.premiumSetting == '2'">
+          <ElDivider style="margin: 10px 0;"></ElDivider>
+          <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+            <span class="strong">保费设置</span>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-table :data="productform.settingList" style="width: 100%;">
+              <el-table-column prop="seats" label="座位数" width="180">
+                <template #default="scope">
+                  <el-input-number v-model="scope.row.seats" controls-position="right" style="width: 100%;" :min="5"
+                    :max="10">
+                    <template #suffix>
+                      <span>座</span>
+                    </template>
+                  </el-input-number>
+                </template>
+              </el-table-column>
+              <el-table-column prop="coverage" label="保额" width="180">
+                <template #default="scope">
+                  <el-input-number v-model="scope.row.coverage" :precision="2" controls-position="right"
+                    style="width: 100%;" :min="1" :max="9000000">
+                    <template #suffix>
+                      <span>元</span>
+                    </template>
+                  </el-input-number>
+                </template>
+              </el-table-column>
+              <el-table-column prop="premium" label="保费">
+                <template #default="scope">
+                  <el-input-number v-model="scope.row.premium" :precision="2" controls-position="right"
+                    style="width: 100%;" :min="1" :max="9000000">
+                    <template #suffix>
+                      <span>元</span>
+                    </template>
+                  </el-input-number>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作">
+                <template #default="scope">
+                  <el-button type="primary" link @click="settingDel(scope.$index)">删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+            <ElButton type="primary" style="margin-top: 10px;" @click="settingListAdd">
+              <template #icon>
+                <el-icon>
+                  <Plus />
+                </el-icon>
+              </template>
+              添加一条
+            </ElButton>
+          </ElCol>
+        </ElCol>
+        <ElCol :md="24" :lg="24">
+          <ElDivider style="margin: 10px 0;"></ElDivider>
+          <ElCol :md="24" :lg="24" style="padding-bottom: 10px;font-size: 14px;">
+            <span class="strong">保险责任</span>
+          </ElCol>
+          <el-table :data="productform.blameList" style="width: 100%;">
+            <el-table-column prop="blame" label="保险责任"></el-table-column>
+            <el-table-column prop="blameCode" label="保险责任编码" width="180" align="center"></el-table-column>
+            <el-table-column prop="blameCoverage" label="保险责任保额" width="180" align="center"></el-table-column>
+            <el-table-column prop="blamePremium" label="保险责任保费" width="180" align="center"></el-table-column>
+            <el-table-column label="操作" width="280" align="center">
+              <template #default="scope">
+                <el-button type="primary" link @click="blameDel(scope.$index)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          <ElButton type="primary" style="margin-top: 10px;" @click="blameListAdd">
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+            添加一条
+          </ElButton>
+        </ElCol>
+      </ElRow>
+    </PageMain>
+    <FixedActionBar>
+      <ElButton @click='router.back()'>
+        取消
+      </ElButton>
+      <ElButton type="primary" @click="submitForm(productFormRef)">
+        保存
+      </ElButton>
+      <ElButton type="primary" @click="submitForm1(productFormRef)">
+        保存并继续添加
+      </ElButton>
+    </FixedActionBar>
+    <el-dialog v-model="blamedialogVisible" title="添加驾意险责任" width="50%">
+      <ElRow :gutter="20">
+        <el-form :model="blameFrom" :inline="true" label-width="120px" :rules="blameFromrules" label-position="top"
+          ref="blameFormRef" style="width: 100%;" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="保险责任" prop="blame">
+              <el-input v-model="blameFrom.blame" placeholder="请输入保险责任" show-word-limit maxlength="50" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="责任编码" prop="blameCode">
+              <el-input v-model="blameFrom.blameCode" placeholder="请输入责任编码" />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="总保额" prop="blameCoverage">
+              <el-input-number v-model="blameFrom.blameCoverage" :precision="2" controls-position="right"
+                style="width: 100%;" :min="1" :max="9000000">
+                <template #suffix>
+                  <span>元</span>
+                </template>
+              </el-input-number>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="总保费" prop="blamePremium">
+              <el-input-number v-model="blameFrom.blamePremium" :precision="2" controls-position="right"
+                style="width: 100%;" :min="1" :max="9000000">
+                <template #suffix>
+                  <span>元</span>
+                </template>
+              </el-input-number>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="blamedialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="blamesbimit(blameFormRef)">
+            保存
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+import { FormRules, FormInstance, ComponentSize, ElMessage } from 'element-plus';
+import { useRoute } from 'vue-router';
+import api from '@/api';
+import router from '@/router';
+import * as Types from '@/api/types/productTypes';
+const route = useRoute();
+const companyId = ref(route.query.companyID as string)//保险公司ID
+const companyName = ref(route.query.companyName as string)//保险公司名称
+const id = ref(route.query.id as string)//id标识
+const type = ref(route.query.type as string)//id标识
+const productType = ref(route.query.productType as string)
+// const dictLists = ref<{ [key: string]: any[] }>({});
+const parentList = ref<parentList>();
+const productFormRef = ref<FormInstance>()
+const blameFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('default')
+const blamedialogVisible = ref(false);
+const productform = reactive<Types.jyxConfigRequest>({
+  id: "",
+  productType: "",//产品类型
+  parentId: "",//上级产品
+  productName: "",//产品名称
+  alias: '',//别名
+  company: "",//保险公司
+  companyName: "",
+  productCode: "",//产品编码
+  connectionMethod: "",//对接方式
+  premiumSetting: "",//保费设置
+  coverage: "",//总保额
+  premium: "",//总保费
+  settingList: [],
+  blameList: [],
+
+})
+const blameFrom = reactive({//保险责任表单弹窗
+  blame: "",
+  blameCode: '',
+  blameCoverage: 0,
+  blamePremium: 0,
+})
+interface parentList {
+  productName: string,
+  id: string,
+  [property: string]: any
+}
+const blameFromrules = ref<FormRules>({
+  blame: [
+    { required: true, trigger: 'blur', message: '请输入保险责任名称' },
+    { max: 50, message: '限制50个字符', trigger: 'blur' },
+
+  ],
+  blameCode: [
+    { required: true, trigger: 'blur', message: '请输入产品编码' },
+  ],
+  blameCoverage: [
+    { required: true, trigger: 'blur', message: '请输入总保额' },
+  ],
+  blamePremium: [
+    { required: true, trigger: 'blur', message: '请输入总保费' },
+  ],
+
+});
+const productformRules = ref<FormRules>({
+  productType: [
+    { required: true, trigger: 'change', message: '请选择产品类型' },
+  ],
+  parentId: [
+    { required: true, trigger: 'change', message: '请选择上级产品' },
+  ],
+  productName: [
+    { required: true, trigger: 'blur', message: '请输入产品名称' },
+  ],
+  company: [
+    { required: true, trigger: 'change', message: '请选择保险公司' },
+  ],
+  productCode: [
+    { required: true, trigger: 'blur', message: '请输入产品编码' },
+  ],
+  connectionMethod: [
+    { required: true, trigger: 'change', message: '请选择对接方式' },
+  ],
+  premiumSetting: [
+    { required: true, trigger: 'change', message: '请选择保费设置' },
+  ],
+  coverage: [
+    { required: true, trigger: 'blur', message: '请输入总保额' },
+  ],
+  premium: [
+    { required: true, trigger: 'blur', message: '请输入总保费' },
+  ],
+});
+
+onBeforeMount(() => {
+  if (companyId.value) {
+    productform.company = companyId.value;
+    productform.companyName = companyName.value;
+  }
+  if (productType.value) {
+    productform.productType = productType.value;
+    productform.parentId = id.value;
+    selectMainIns();
+  }
+  if (id.value && type.value == 'edit') {
+    detalis(id.value);
+  }
+
+});
+const settingListAdd = () => {
+  productform.settingList.push({
+    seats: 0,
+    coverage: 0,
+    premium: 0,
+  })
+}
+const settingDel = (index: any) => {
+  productform.settingList.splice(index, 1);
+}
+const blameListAdd = () => {
+  blamedialogVisible.value = true;
+
+}
+const blameDel = (index: any) => {
+  productform.blameList.splice(index, 1);
+
+}
+//类型事件
+const productTypeChange = (value: any) => {
+  if (value == 2) {
+    selectMainIns();
+  }
+}
+//查询主险
+const selectMainIns = () => {
+  api.product.selectMainInsurance(companyId.value).then((res: any) => {
+    if (res.code == '200') {
+      parentList.value = res.data;
+    } else {
+    }
+  });
+}
+const detalis = (id: string) => {
+  api.product.jygetById(id).then((res: any) => {
+    if (res.code == '200') {
+      selectMainIns();
+      Object.keys(productform).forEach((key) => {
+        if (res.data.hasOwnProperty(key)) {
+          productform[key as keyof typeof productform] = res.data[key as keyof typeof productform];
+        }
+      });
+      if (Array.isArray(res.data.settingList)) {
+        productform.settingList = res.data.settingList.map((item: any) => ({
+          seats: item.seats,
+          coverage: item.coverage,
+          premium: item.premium,
+        }));
+      }
+      if (Array.isArray(res.data.blameList)) {
+        productform.blameList = res.data.blameList.map((item: any) => ({
+          blame: item.blame,
+          blameCode: item.blameCode,
+          blameCoverage: item.blameCoverage,
+          blamePremium: item.blamePremium,
+        }));
+      }
+    } else {
+    }
+  });
+}
+//字典信息处理
+// 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 submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (id.value && type.value == 'edit') {
+        api.product.jyxupdateById(productform).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back()
+
+          }
+        })
+      } else {
+        api.product.jyxConfigAdd(productform).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.back()
+          }
+        })
+      }
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+const submitForm1 = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (id.value && type.value == 'edit') {
+        api.product.jyxupdateById(productform).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.push({
+              name: 'reload',
+            })
+          }
+        })
+      } else {
+        api.product.jyxConfigAdd(productform).then((res: any) => {
+          if (res.code == 200) {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            router.push({
+              name: 'reload',
+            })
+          }
+        })
+      }
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+
+const blamesbimit = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      productform.blameList.push({ ...blameFrom })
+      blamedialogVisible.value = false;
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+</script>

+ 240 - 0
src/views/productConfiguration/drivingInsurance/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <ElRow :gutter="20" style="height: 100%;">
+        <ElCol :md="4">
+          <div class="flex f-c " style="height: 100%;">
+            <ElFormItem>
+              <ElInput v-model="companyName" placeholder="输入保险公司名称查找" type="text" tabindex="1" clearable style="width: 100%;"  @clear="companyQuery">
+                <template #append>
+                  <el-button :icon="Search" @click="companyQuery" v-auth="'drivingInsurance.add'" />
+                </template>
+              </ElInput>
+            </ElFormItem>
+            <div style=" width: 100%;height: calc(100vh - 310px);  overflow-y: auto;" class="container ">
+              <div class="pad-1 roleitem " :class="{ active: companyactiveID === item.id }"
+                v-for="(item, index) in companyList" :key="index" @click="roleitemClick(item)">
+                <span>{{ item.name }}</span>
+              </div>
+            </div>
+          </div>
+        </ElCol>
+        <ElCol :md="1" style="flex: unset;">
+          <ElDivider direction="vertical" style="height: 100%;"></ElDivider>
+        </ElCol>
+        <ElCol :md="19">
+          <div class="flex j-s a-c">
+            <span class="strong">驾意险配置</span>
+            <RouterLink
+              :to="{ name: 'DrivingInsuranceAdd', query: { companyID: companyactiveID, companyName: companyactiveName } }">
+              <ElButton type="default" v-if="companyactiveID">
+                <template #icon>
+                  <el-icon>
+                    <Plus />
+                  </el-icon>
+                </template>
+                添加驾意险
+              </ElButton>
+            </RouterLink>
+
+          </div>
+          <ElDivider style="margin: 10px 0;"></ElDivider>
+          <div class="flex f-c elementSpacing">
+            <span class="strong">{{ companyactiveName ? companyactiveName : '-------------' }}</span>
+            <span style="font-size: 12px;">ID: {{ companyactiveID ? companyactiveID : '-------------' }} </span>
+            <el-form :inline="true" :model="formInline" class="demo-form-inline">
+              <el-form-item>
+                <el-input v-model="formInline.params" placeholder="输入产品名称,别名查找" clearable />
+              </el-form-item>
+              <el-form-item>
+                <el-input v-model="formInline.seats" placeholder="输入座位数查找" clearable>
+                  <template #append>座</template>
+                </el-input>
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" @click="findpage" v-auth="'drivingInsurance.view'">查询</el-button>
+                <el-button type="default" @click="reset" v-auth="'drivingInsurance.view'">重置</el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+          <ElDivider style="margin: 10px 0;"></ElDivider>
+          <TableTree :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200"
+            :showSelect="false">
+            <template v-slot:operation="scope">
+              <el-button type="primary" link style="margin-right: 15px;" v-if="scope.operationData.parentId == '0'"
+                @click="add(scope.operationData)" v-auth="'drivingInsurance.add'"> 添加
+              </el-button>
+              <el-button type="primary" link style="margin-right: 15px;" @click="edit(scope.operationData.id)" v-auth="'drivingInsurance.edit'"> 编辑
+              </el-button>
+              <el-button type="primary" link style="margin-right: 15px;" @click="del(scope.operationData)" v-auth="'drivingInsurance.delete'"> 删除
+              </el-button>
+            </template>
+          </TableTree>
+        </ElCol>
+      </ElRow>
+
+    </PageMain>
+  </div>
+</template>
+
+<script lang='ts' setup>
+import api from '@/api';
+import { RouterLink, useRouter } from "vue-router";
+import { ref, reactive, onBeforeMount } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { Search } from '@element-plus/icons-vue'
+onBeforeMount(() => {
+  companyQuery();
+});
+interface companyList {
+  nameSimple: string,
+  name: string,
+  id: string,
+  [key: string]: any
+}
+const router = useRouter();
+const companyName = ref<string>("")//保险公司检索条件
+const companyList = ref<companyList>();
+const companyactiveID = ref("");
+const companyactiveName = ref("")
+const tableData = ref([]);//驾意险数据
+const formInline = reactive<{
+  params: string,
+  seats: string,
+}>({
+  params: "",//名称、别名
+  seats: "",//座位数
+});
+const columns = [
+  { prop: "productName", label: "产品名称", align: 'left' },
+  { prop: "productCode", label: "产品编码", width: '180' },
+  { prop: "alias", label: "产品别名", width: '180' },
+
+];
+const reset=()=>{
+  formInline.params="";
+  formInline.seats="";
+}
+//获取驾意险
+const findpage = () => {
+  api.product.jyxList({ company: companyactiveID.value,...formInline }).then((res: any) => {
+    if (res.code == '200') {
+      tableData.value = res.data;
+    } else {
+    }
+  });
+}//获取保险公司列表
+const companyQuery = () => {
+  api.company.selectLevelList({ name: companyName.value }).then((res: any) => {
+    if (res.code == '200') {
+      companyList.value = res.data;
+    }
+  });
+}
+//租户列表事件
+async function roleitemClick(item: { id: string, name: string }) {
+  companyactiveID.value = item.id;
+  companyactiveName.value = item.name;
+  findpage();
+}
+const edit = (id: any) => {
+  router.push({
+    name: 'DrivingInsuranceAdd',
+    query: {
+      type: 'edit',
+      id: id,
+      companyID: companyactiveID.value,
+      companyName: companyactiveName.value,
+    }
+  })
+
+}
+
+const add = (item: any) => {
+  // return ;
+  router.push({
+    name: 'DrivingInsuranceAdd',
+    query: {
+      type: 'add',
+      id: item.id,
+      productType: '2',
+      companyID: companyactiveID.value,
+      companyName: companyactiveName.value,
+    }
+  })
+}
+const del = (item: any) => {
+  ElMessageBox.confirm(
+    item.parentId == '0' ? '该驾意险下有附加驾意险,删除该驾意险会连同附加驾意险一并删除,请确定是否删除?' : '删除后不可恢复,请谨慎操作',
+    '确定要删除该驾意险吗?',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.product.deleteById(item.id).then((res: any) => {
+        if (res.code == '200') {
+          findpage();
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+        } else {
+        }
+      });
+    })
+    .catch(() => {
+    })
+}
+</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>

+ 318 - 0
src/views/productConfiguration/specialClause/index.vue

@@ -0,0 +1,318 @@
+<template>
+  <div>
+    <PageMain class="vh100">
+      <ElForm :inline="false" :model="formInline" class="demo-form-inline" label-width="100px">
+        <ElRow :gutter="20">
+          <ElCol :md="6">
+            <ElFormItem label="特约名称">
+              <el-input v-model="formInline.companyName" placeholder="输入保险公司查找" clearable class="el-inputs" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="6">
+            <ElFormItem label="特约内容">
+              <el-input v-model="formInline.companyName" placeholder="输入保险公司查找" clearable class="el-inputs" />
+            </ElFormItem>
+          </ElCol>
+          <ElCol :md="1">
+            <el-button type="primary" @click="findpage">查询</el-button>
+          </ElCol>
+        </ElRow>
+      </ElForm>
+      <ElDivider border-style="dashed"></ElDivider>
+      <Table :tableData="tableData" :columns="columns" :showIndex="false" :columnwidth="200" :showSelect="false"
+        :pageInfo="pageInfo" @getList="getList">
+        <template v-slot:table-title-btn>
+          <el-button type="primary" @click="companyAdd">
+            添加特约
+            <template #icon>
+              <el-icon>
+                <Plus />
+              </el-icon>
+            </template>
+          </el-button>
+        </template>
+        <template v-slot:operation="scope">
+          <el-button type="primary" link @click="paymentEdit(scope.operationData.id)"> 编辑
+          </el-button>
+          <el-button type="primary" link @click="paymentDel(scope.operationData.id)"> 删除
+          </el-button>
+        </template>
+        <template v-slot:search="scope">
+          {{ scope.slotData.label }}
+        </template>
+      </Table>
+    </PageMain>
+
+    <el-dialog v-model="paynemtAdddialogVisible" :title="dialogTitle" width="40%" @close="close">
+      <ElRow :gutter="20">
+        <el-form :model="paymentform" :inline="true" label-width="120px" :rules="memberformRules" label-position="top"
+          ref="paymentFormRef" style="width: 100%;" :size="fromSize">
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="保险公司" prop="companyId">
+              <el-select v-model="paymentform.companyId" @change="companyIdChange" placeholder="请选择"
+                style="width: 100%;">
+                <el-option :label="item.name" :value="item.id" v-for="(item, index) in companyList" :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="简称" prop="companyName">
+              <el-input v-model="paymentform.companyName" placeholder="请输入姓名" disabled />
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="二维码类型" prop="type">
+              <el-select v-model="paymentform.type" placeholder="请选择" style="width: 100%;">
+                <el-option v-for="(item, index) in getDict('qr_barcode')" :label="item.label" :value="item.value"
+                  :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+          <ElCol :md="24" :lg="12">
+            <el-form-item label="请求类型" prop="apiType">
+              <el-select v-model="paymentform.apiType" placeholder="请选择" style="width: 100%;">
+                <el-option v-for="(item, index) in getDict('api_type')" :label="item.label" :value="item.value"
+                  :key="index" />
+              </el-select>
+            </el-form-item>
+          </ElCol>
+        </el-form>
+      </ElRow>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="paynemtAdddialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="MemberAdd(paymentFormRef)">
+            保存
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import api from '@/api';
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { FormRules, FormInstance, ComponentSize } from 'element-plus';
+import { loadDicts, getDict } from '@/utils/composables/dictService';
+interface PageInfo {
+  pageNum: number;
+  pageSize: number;
+  sizes: number[];
+  total: number;
+}
+interface paymentform {
+  id?: string,
+  companyId: string,
+  companyName: string,
+  type: string,
+  apiType: string,
+  companyCode: string,
+}
+
+const tableData = ref([]);//列表数据
+const paymentFormRef = ref<FormInstance>()
+const fromSize = ref<ComponentSize>('large')//表单大小
+const paynemtAdddialogVisible = ref(false)//弹窗
+const dictLists = ref<{ [key: string]: any[] }>({});//字典集合
+const dialogTitle = ref('支付码配置')//弹窗title
+const dialogType = ref('add')//弹窗title
+const companyList = ref();
+const paymentform = reactive<paymentform>({
+  id: "",
+  companyId: "",
+  companyName: "",
+  type: "",
+  apiType: "",
+  companyCode: "",
+})
+const memberformRules = ref<FormRules>({
+  companyId: [
+    { required: true, trigger: 'change', message: '请选择保险公司' },
+  ],
+  type: [
+    { required: true, trigger: 'change', message: '请选择二维码类型' },
+  ],
+  apiType: [
+    { required: true, trigger: 'change', message: '请选择请求类型' },
+  ],
+
+});
+//分页
+const pageInfo = ref({
+  pageNum: 1,
+  pageSize: 20,
+  sizes: [10, 20, 30, 40, 50],
+  total: 0,
+});
+//检索条件
+const formInline = reactive({
+  companyName: '',//保险公司name
+})
+onMounted(() => {
+  findpage();
+  companyQuery();//获取保险公司
+  loadDicts(['qr_barcode', 'api_type']);//获取多个字典
+});
+function findpage() {
+  api.company.QRcodePage({ pages: 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 columns = [
+  { prop: "companyName", label: "特约名称", align: 'left' },
+  { prop: "companyCode", label: "编码", width: '220' },
+  {
+    prop: "type", label: "特别约定内容", width: '280', formatter: (row: any) => {
+      let type = row.type;
+      let name = getDict('qr_barcode').find(val => val.value == type).label;
+      return name;
+    }
+  },
+];
+function getList(item: PageInfo) {
+  pageInfo.value.pageNum = item.pageNum;
+  pageInfo.value.pageSize = item.pageSize;
+  findpage();
+};
+const companyIdChange = (value: string) => {
+  let Info = companyList.value.find((val: any) => val.id == value);
+  paymentform.companyName = Info.name;
+  paymentform.companyCode = Info.companyCode;
+}
+//获取保险公司列表
+const companyQuery = () => {
+  api.company.selectLevelList({ name: "" }).then((res: any) => {
+    if (res.code == '200') {
+      companyList.value = res.data;
+    }
+  });
+}
+const companyAdd = () => {
+  dialogTitle.value = '支付码配置';
+  dialogType.value = 'add';
+  paynemtAdddialogVisible.value = true
+}
+const MemberAdd = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      if (dialogType.value == 'add') {
+        api.company.QRBarcodeAdd({ ...paymentform }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            paynemtAdddialogVisible.value = false;
+            findpage();
+          } else {
+          }
+        });
+      } else {
+        api.company.QRBarcodeUpdate({ ...paymentform }).then((res: any) => {
+          if (res.code == '200') {
+            ElMessage({
+              type: 'success',
+              message: res.msg,
+              plain: true,
+            })
+            paynemtAdddialogVisible.value = false;
+            findpage();
+          } else {
+          }
+        });
+      }
+    } else {
+      ElMessage({
+        type: 'error',
+        message: '信息不完整',
+        plain: true,
+      })
+    }
+  })
+}
+// 编辑
+const paymentEdit = (id: string) => {
+  dialogTitle.value = '支付码编辑';
+  dialogType.value = 'edit';
+  api.company.QRcodeById(id).then((res: any) => {
+    if (res.code == '200') {
+      Object.keys(paymentform).forEach((key) => {
+        if (res.data.hasOwnProperty(key)) {
+          paymentform[key as keyof typeof paymentform] = res.data[key as keyof typeof paymentform];
+        }
+      });
+      paynemtAdddialogVisible.value = true;
+    } else {
+    }
+  });
+}
+// 删除
+const paymentDel = (id: string) => {
+  ElMessageBox.confirm(
+    '确定删除吗?',
+    '删除二维码配置',
+    {
+      confirmButtonText: '确定删除',
+      type: 'error',
+      center: true,
+      confirmButtonClass: 'el-button--danger',
+    }
+  )
+    .then(() => {
+      api.company.QRcodeDel(id).then((res: any) => {
+        if (res.code == '200') {
+          ElMessage({
+            type: 'success',
+            message: res.msg,
+            plain: true,
+          })
+          findpage();
+        } else {
+        }
+      });
+    })
+    .catch(() => {
+    })
+
+}
+//字典信息处理
+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 close = () => {
+  paymentform.companyId = "";
+  paymentform.companyName = "";
+  paymentform.type = "";
+  paymentform.apiType = "";
+}
+</script>
+<style lang="scss" scoped>
+:deep(.el-divider--horizontal) {
+  margin: 8px 0 24px;
+  color: #f2f2f2;
+}
+
+.el-inputs {
+  width: 100% !important;
+}
+</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',