liuc 4 месяцев назад
Родитель
Сommit
b897adc7eb
38 измененных файлов с 2972 добавлено и 42 удалено
  1. 1 0
      .env.development
  2. 1 0
      .env.production
  3. 329 19
      package-lock.json
  4. 2 0
      package.json
  5. BIN
      src/assets/back.png
  6. BIN
      src/assets/bgAdmin.png
  7. BIN
      src/assets/file.png
  8. BIN
      src/assets/form/ask.png
  9. BIN
      src/assets/form/filling1.png
  10. BIN
      src/assets/form/filling2.png
  11. BIN
      src/assets/form/finish.png
  12. 1 0
      src/assets/form/finish.svg
  13. BIN
      src/assets/form/going.png
  14. 1 0
      src/assets/form/right.svg
  15. BIN
      src/assets/place.png
  16. BIN
      src/assets/switch.png
  17. BIN
      src/assets/time.png
  18. 24 0
      src/components/MonacoEditor/EditorWorker.vue
  19. 6 5
      src/components/MonacoEditor/index.hook.js
  20. 4 0
      src/components/MonacoEditor/index.js
  21. 98 0
      src/components/MonacoEditor/index.vue
  22. 360 0
      src/components/common/common-card/index.vue
  23. 36 0
      src/components/common/common-drawer/index copy.vue
  24. 36 0
      src/components/common/common-drawer/index.vue
  25. 149 0
      src/components/common/common-form/index.vue
  26. 130 0
      src/components/common/common-table-no-page/components/search.vue
  27. 520 0
      src/components/common/common-table-no-page/components/table.vue
  28. 130 0
      src/components/common/common-table-no-page/example.js
  29. 90 0
      src/components/common/common-table-no-page/index.vue
  30. 134 0
      src/components/common/common-table/components/search.vue
  31. 575 0
      src/components/common/common-table/components/table.vue
  32. 130 0
      src/components/common/common-table/example.js
  33. 135 0
      src/components/common/common-table/index.vue
  34. 3 0
      src/constants/common-const.js
  35. 24 1
      src/main.js
  36. 27 1
      src/router/flow/index.js
  37. 24 14
      src/router/index.js
  38. 2 2
      src/store/modules/websocket.js

+ 1 - 0
.env.development

@@ -6,3 +6,4 @@ VITE_APP_API_BASE_URL='http://localhost:8081/#'
 VITE_APP_API_FILE_VIEW_URL='http://192.168.17.153:8812'
 VITE_APP_API_CUSTOM_URL='/base-api'
 VITE_APP_DING_APP_ID='dingu35ilmyp0f4vlkfd'
+VITE_APP_API_WEBSOCKET='ws://27.223.11.42:9100/scene'

+ 1 - 0
.env.production

@@ -6,3 +6,4 @@ VITE_APP_API_BASE_URL='http://27.223.11.42:9102/#'
 VITE_APP_API_FILE_VIEW_URL='http://27.223.11.42:8812'
 VITE_APP_API_CUSTOM_URL='/common-api'
 VITE_APP_DING_APP_ID='123456789'
+VITE_APP_API_WEBSOCKET='ws://27.223.11.42:9100/scene'

+ 329 - 19
package-lock.json

@@ -41,6 +41,7 @@
         "diff2html": "3.4.47",
         "echarts": "5.4.3",
         "highlight.js": "11.8.0",
+        "js-base64": "^3.7.7",
         "js-cookie": "^3.0.5",
         "jslint": "^0.12.1",
         "lodash": "^4.17.21",
@@ -62,12 +63,13 @@
         "vue": "3.4.27",
         "vue-codemirror-lite": "^1.0.4",
         "vue-i18n": "9.13.1",
+        "vue-plugin-hiprint": "^0.0.60",
         "vue-quill-editor": "^3.0.6",
         "vue-router": "4.3.2",
         "vue3-json-viewer": "2.2.2",
         "vue3-tabs-chrome": "^0.3.3",
         "vuedraggable": "^4.1.0",
-        "vxe-table": "^4.7.80"
+        "vxe-table": "^4.13.37"
       },
       "devDependencies": {
         "@vitejs/plugin-vue": "5.0.4",
@@ -863,6 +865,14 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@claviska/jquery-minicolors": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmmirror.com/@claviska/jquery-minicolors/-/jquery-minicolors-2.3.6.tgz",
+      "integrity": "sha512-8Ro6D4GCrmOl41+6w4NFhEOpx8vjxwVRI69bulXsFDt49uVRKhLU5TnzEV7AmOJrylkVq+ugnYNMiGHBieeKUQ==",
+      "peerDependencies": {
+        "jquery": ">= 1.7.x"
+      }
+    },
     "node_modules/@ctrl/tinycolor": {
       "version": "3.6.1",
       "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
@@ -2322,6 +2332,11 @@
         "nanopop": "^2.1.0"
       }
     },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
+    },
     "node_modules/@transloadit/prettier-bytes": {
       "version": "0.0.7",
       "resolved": "https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz",
@@ -2395,6 +2410,11 @@
       "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
       "dev": true
     },
+    "node_modules/@types/raf": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz",
+      "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw=="
+    },
     "node_modules/@types/web-bluetooth": {
       "version": "0.0.16",
       "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
@@ -2779,12 +2799,15 @@
       }
     },
     "node_modules/@vxe-ui/core": {
-      "version": "4.0.12",
-      "resolved": "https://registry.npmmirror.com/@vxe-ui/core/-/core-4.0.12.tgz",
-      "integrity": "sha512-ft8f874eQSv4N9+oulFKeg8APgd8RMHeFeUUUTNckIRJ/cNi0dbR0Fe2+ZZpRl3BwRtbE2hHb2FKWmL2oyZkfw==",
+      "version": "4.1.5",
+      "resolved": "https://registry.npmmirror.com/@vxe-ui/core/-/core-4.1.5.tgz",
+      "integrity": "sha512-IgRwVueejOGC5t+bVmBAUkoUplvp1R77pfYX6bb4fcLEPUdBGOdm4I0LCKTDWQ24Mj3Bki7wNpt3sdtEZEzdoA==",
       "dependencies": {
         "dom-zindex": "^1.0.6",
-        "xe-utils": "^3.5.30"
+        "xe-utils": "^3.7.5"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
       }
     },
     "node_modules/@wangeditor/basic-modules": {
@@ -3098,6 +3121,18 @@
         "@xtuc/long": "4.2.2"
       }
     },
+    "node_modules/@wtto00/html2canvas": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmmirror.com/@wtto00/html2canvas/-/html2canvas-1.4.3.tgz",
+      "integrity": "sha512-jwsb+xL8N+gjrSNABSaFdxmWtE4c7RNFjP20lo1G7gs63Qqo1phhxVBTzxc/apDVh6LgXsU2l5bwKtXd9uz65w==",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/@xtuc/ieee754": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -3443,6 +3478,17 @@
       "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
       "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
     },
+    "node_modules/atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "bin": {
+        "atob": "bin/atob.js"
+      },
+      "engines": {
+        "node": ">= 4.5.0"
+      }
+    },
     "node_modules/axios": {
       "version": "1.6.8",
       "resolved": "https://registry.npmmirror.com/axios/-/axios-1.6.8.tgz",
@@ -3458,6 +3504,14 @@
       "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
     },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
     "node_modules/boolbase": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
@@ -3517,6 +3571,17 @@
         "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
       }
     },
+    "node_modules/btoa": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/btoa/-/btoa-1.2.1.tgz",
+      "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
+      "bin": {
+        "btoa": "bin/btoa.js"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
     "node_modules/buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3528,6 +3593,14 @@
       "resolved": "https://registry.npmmirror.com/buffer-shims/-/buffer-shims-1.0.0.tgz",
       "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g=="
     },
+    "node_modules/bwip-js": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmmirror.com/bwip-js/-/bwip-js-4.6.0.tgz",
+      "integrity": "sha512-Djr1aQ3d1N8rpLz5XgbpNW/yrP4owC+rk5/pZTSzkqXY0WvqzJ0yJTxA8JJA6WDxOAP1hP70AcnTxiDvthy+/g==",
+      "bin": {
+        "bwip-js": "bin/bwip-js.js"
+      }
+    },
     "node_modules/call-bind": {
       "version": "1.0.7",
       "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz",
@@ -3601,6 +3674,29 @@
         }
       ]
     },
+    "node_modules/canvg": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz",
+      "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
+      "dependencies": {
+        "@babel/runtime": "^7.12.5",
+        "@types/raf": "^3.4.0",
+        "core-js": "^3.8.3",
+        "raf": "^3.4.1",
+        "regenerator-runtime": "^0.13.7",
+        "rgbcolor": "^1.0.1",
+        "stackblur-canvas": "^2.0.0",
+        "svg-pathdata": "^6.0.3"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/canvg/node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
+    },
     "node_modules/chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
@@ -3833,6 +3929,14 @@
         "node": ">=12 || >=16"
       }
     },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/cssesc": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz",
@@ -3871,7 +3975,6 @@
       "version": "4.3.7",
       "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz",
       "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
-      "dev": true,
       "dependencies": {
         "ms": "^2.1.3"
       },
@@ -4103,6 +4206,12 @@
         "ssr-window": "^3.0.0-alpha.1"
       }
     },
+    "node_modules/dompurify": {
+      "version": "2.5.8",
+      "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-2.5.8.tgz",
+      "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==",
+      "optional": true
+    },
     "node_modules/draggabilly": {
       "version": "2.4.1",
       "resolved": "https://registry.npmmirror.com/draggabilly/-/draggabilly-2.4.1.tgz",
@@ -4139,6 +4248,26 @@
       "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
       "dev": true
     },
+    "node_modules/engine.io-client": {
+      "version": "6.6.3",
+      "resolved": "https://registry.npmmirror.com/engine.io-client/-/engine.io-client-6.6.3.tgz",
+      "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1",
+        "xmlhttprequest-ssl": "~2.1.1"
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/enhanced-resolve": {
       "version": "5.18.1",
       "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
@@ -4775,6 +4904,11 @@
         "reusify": "^1.0.4"
       }
     },
+    "node_modules/fflate": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz",
+      "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="
+    },
     "node_modules/file-entry-cache": {
       "version": "6.0.1",
       "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5268,6 +5402,19 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "optional": true,
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
     "node_modules/i18next": {
       "version": "20.6.1",
       "resolved": "https://registry.npmmirror.com/i18next/-/i18next-20.6.1.tgz",
@@ -5612,6 +5759,16 @@
         "url": "https://github.com/chalk/supports-color?sponsor=1"
       }
     },
+    "node_modules/jquery": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz",
+      "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
+    },
+    "node_modules/js-base64": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.7.tgz",
+      "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="
+    },
     "node_modules/js-cookie": {
       "version": "3.0.5",
       "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz",
@@ -5637,6 +5794,11 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/jsbarcode": {
+      "version": "3.12.1",
+      "resolved": "https://registry.npmmirror.com/jsbarcode/-/jsbarcode-3.12.1.tgz",
+      "integrity": "sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ=="
+    },
     "node_modules/jsbn": {
       "version": "1.1.0",
       "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz",
@@ -5728,6 +5890,23 @@
         "node": ">=6"
       }
     },
+    "node_modules/jspdf": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmmirror.com/jspdf/-/jspdf-2.5.2.tgz",
+      "integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.23.2",
+        "atob": "^2.1.2",
+        "btoa": "^1.2.1",
+        "fflate": "^0.8.1"
+      },
+      "optionalDependencies": {
+        "canvg": "^3.0.6",
+        "core-js": "^3.6.0",
+        "dompurify": "^2.5.4",
+        "html2canvas": "^1.0.0-rc.5"
+      }
+    },
     "node_modules/keyv": {
       "version": "4.5.4",
       "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz",
@@ -6177,8 +6356,7 @@
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
-      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
-      "dev": true
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
     "node_modules/namespace-emitter": {
       "version": "2.0.1",
@@ -6322,6 +6500,11 @@
         "url": "https://github.com/fb55/nth-check?sponsor=1"
       }
     },
+    "node_modules/nzh": {
+      "version": "1.0.14",
+      "resolved": "https://registry.npmmirror.com/nzh/-/nzh-1.0.14.tgz",
+      "integrity": "sha512-wKgaqCSZdrySvB4RWop5g+v6IDv2IErsT6rjq06Bg0yiT9hiHYZO12GMGx/xweGVLcO2lDjX5RqWD0S/Jy9z5Q=="
+    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
@@ -6556,6 +6739,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
+    },
     "node_modules/picocolors": {
       "version": "1.1.0",
       "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.0.tgz",
@@ -6855,6 +7043,14 @@
         "node": ">=0.10"
       }
     },
+    "node_modules/raf": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz",
+      "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
+      "dependencies": {
+        "performance-now": "^2.1.0"
+      }
+    },
     "node_modules/randombytes": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
@@ -8322,6 +8518,14 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/rgbcolor": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz",
+      "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
+      "engines": {
+        "node": ">= 0.8.15"
+      }
+    },
     "node_modules/rimraf": {
       "version": "5.0.10",
       "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-5.0.10.tgz",
@@ -8761,6 +8965,32 @@
         "node": ">=12.17.0"
       }
     },
+    "node_modules/socket.io-client": {
+      "version": "4.8.1",
+      "resolved": "https://registry.npmmirror.com/socket.io-client/-/socket.io-client-4.8.1.tgz",
+      "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.6.1",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/sortablejs": {
       "version": "1.15.0",
       "resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.0.tgz",
@@ -8839,6 +9069,14 @@
       "resolved": "https://registry.npmmirror.com/ssr-window/-/ssr-window-3.0.0.tgz",
       "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA=="
     },
+    "node_modules/stackblur-canvas": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
+      "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
+      "engines": {
+        "node": ">=0.1.14"
+      }
+    },
     "node_modules/store": {
       "version": "2.0.12",
       "resolved": "https://registry.npmmirror.com/store/-/store-2.0.12.tgz",
@@ -9178,6 +9416,14 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/svg-pathdata": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
+      "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/svg-tags": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz",
@@ -9346,6 +9592,14 @@
         "node": ">=10"
       }
     },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
     "node_modules/text-table": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz",
@@ -9537,6 +9791,14 @@
         "node": ">= 4"
       }
     },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
     "node_modules/uuid": {
       "version": "10.0.0",
       "resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
@@ -9872,6 +10134,26 @@
         "vue": "^3.0.0"
       }
     },
+    "node_modules/vue-plugin-hiprint": {
+      "version": "0.0.60",
+      "resolved": "https://registry.npmmirror.com/vue-plugin-hiprint/-/vue-plugin-hiprint-0.0.60.tgz",
+      "integrity": "sha512-a5uOMn6Nr4qlYYaVNbQKwRZJa8UcNMTflfi6J430/NDtySJB+5ArE8I8+NLjgVV56x3/qdUBs/GWuZCX5Umv1w==",
+      "dependencies": {
+        "@claviska/jquery-minicolors": "^2.3.6",
+        "@wtto00/html2canvas": "^1.4.3",
+        "bwip-js": "^4.0.0",
+        "canvg": "^3.0.10",
+        "jquery": "^3.6.0",
+        "jsbarcode": "^3.11.5",
+        "jspdf": "^2.5.1",
+        "lodash": "^4.17.21",
+        "nzh": "^1.0.8",
+        "socket.io-client": "^4.5.1"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
     "node_modules/vue-quill-editor": {
       "version": "3.0.6",
       "resolved": "https://registry.npmmirror.com/vue-quill-editor/-/vue-quill-editor-3.0.6.tgz",
@@ -9964,19 +10246,19 @@
       "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
     },
     "node_modules/vxe-pc-ui": {
-      "version": "4.2.9",
-      "resolved": "https://registry.npmmirror.com/vxe-pc-ui/-/vxe-pc-ui-4.2.9.tgz",
-      "integrity": "sha512-FxjGhk6hwMloA3lDk+2P6ruIDOaqCCEloU/yx8XtRHLRuTOBlmC+dycHQi4vdt4qg05ssB4QW/wtOrfTNgjlHg==",
+      "version": "4.6.30",
+      "resolved": "https://registry.npmmirror.com/vxe-pc-ui/-/vxe-pc-ui-4.6.30.tgz",
+      "integrity": "sha512-maNduw/kzyzaJ9Vq0Y74djOWexeuFRr5ssWuJ0ZuSmhNqP+ddn+uarvWje2Uk+HKWYtE0+i920bjPbJKH3THAw==",
       "dependencies": {
-        "@vxe-ui/core": "^4.0.12"
+        "@vxe-ui/core": "^4.1.5"
       }
     },
     "node_modules/vxe-table": {
-      "version": "4.7.84",
-      "resolved": "https://registry.npmmirror.com/vxe-table/-/vxe-table-4.7.84.tgz",
-      "integrity": "sha512-dTBI20273NCWZ8Ngb3bFKYtjZiKH73ecbBUy9s4E1W2Xb9OjxKXwMhyi2TxO0OYl1Qxdf2KCwYNov6zuetx/iQ==",
+      "version": "4.13.45",
+      "resolved": "https://registry.npmmirror.com/vxe-table/-/vxe-table-4.13.45.tgz",
+      "integrity": "sha512-Vs/cahd/Idxzv53rYYLJMpWOGyTqgFuld7sc34vqifW8AcSKPFZQjXipCYk4iosI5vTo4sZxnOxMGh9iS2sXHQ==",
       "dependencies": {
-        "vxe-pc-ui": "^4.2.9"
+        "vxe-pc-ui": "^4.6.0"
       }
     },
     "node_modules/warning": {
@@ -10256,10 +10538,30 @@
       "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
       "dev": true
     },
+    "node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmmirror.com/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/xe-utils": {
-      "version": "3.5.30",
-      "resolved": "https://registry.npmmirror.com/xe-utils/-/xe-utils-3.5.30.tgz",
-      "integrity": "sha512-5Ez6JUANpMakduiTLxrNObzqMebnM4697KvHW5okedkUjXvYgGvkbg0tABTkvwDW/Pb09v7vT68dzBOeAuOu0g=="
+      "version": "3.7.5",
+      "resolved": "https://registry.npmmirror.com/xe-utils/-/xe-utils-3.7.5.tgz",
+      "integrity": "sha512-wDjqnXw02EQxf2jqlE1nhvT9HP3PDVcyrol5whDJN/NOvnMyXIzcwEiPB/H2T3aq07f2QQXsSs4Z8g5L3BVH5A=="
     },
     "node_modules/xml-name-validator": {
       "version": "4.0.0",
@@ -10270,6 +10572,14 @@
         "node": ">=12"
       }
     },
+    "node_modules/xmlhttprequest-ssl": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
+      "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/yallist": {
       "version": "3.1.1",
       "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",

+ 2 - 0
package.json

@@ -49,6 +49,7 @@
     "diff2html": "3.4.47",
     "echarts": "5.4.3",
     "highlight.js": "11.8.0",
+    "js-base64": "^3.7.7",
     "js-cookie": "^3.0.5",
     "jslint": "^0.12.1",
     "lodash": "^4.17.21",
@@ -70,6 +71,7 @@
     "vue": "3.4.27",
     "vue-codemirror-lite": "^1.0.4",
     "vue-i18n": "9.13.1",
+    "vue-plugin-hiprint": "^0.0.60",
     "vue-quill-editor": "^3.0.6",
     "vue-router": "4.3.2",
     "vue3-json-viewer": "2.2.2",

BIN
src/assets/back.png


BIN
src/assets/bgAdmin.png


BIN
src/assets/file.png


BIN
src/assets/form/ask.png


BIN
src/assets/form/filling1.png


BIN
src/assets/form/filling2.png


BIN
src/assets/form/finish.png


+ 1 - 0
src/assets/form/finish.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746604893883" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3019" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m-40 644l-80-78.4-160-156.8 80-78.4 160 156.8L712 316l80 78.4L472 708z" fill="#4279f9" p-id="3020"></path></svg>

BIN
src/assets/form/going.png


+ 1 - 0
src/assets/form/right.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1746609643399" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6483" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M881 562H81c-27.6 0-50-22.4-50-50s22.4-50 50-50h800c27.6 0 50 22.4 50 50s-22.4 50-50 50z" fill="#4279f9" p-id="6484"></path><path d="M907.6 540.7L695.5 328.6c-19.5-19.5-19.5-51.2 0-70.7s51.2-19.5 70.7 0L978.4 470c19.5 19.5 19.5 51.2 0 70.7-19.6 19.6-51.2 19.6-70.8 0z" fill="#4279f9" p-id="6485"></path><path d="M695.5 695.4l212.1-212.1c19.5-19.5 51.2-19.5 70.7 0s19.5 51.2 0 70.7L766.2 766.1c-19.5 19.5-51.2 19.5-70.7 0s-19.5-51.2 0-70.7z" fill="#4279f9" p-id="6486"></path></svg>

BIN
src/assets/place.png


BIN
src/assets/switch.png


BIN
src/assets/time.png


+ 24 - 0
src/components/MonacoEditor/EditorWorker.vue

@@ -0,0 +1,24 @@
+<template></template>
+
+<script setup>
+import * as monaco from 'monaco-editor'
+import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
+import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
+import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
+import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
+
+self.MonacoEnvironment = {
+  getWorker(workerId, label) {
+    if (label === 'json') {
+      return new jsonWorker()
+    }
+    if (label === 'typescript' || label === 'javascript') {
+      return new tsWorker()
+    }
+    if (label === 'html') {
+      return new htmlWorker()
+    }
+    return new editorWorker()
+  }
+}
+</script>

+ 6 - 5
src/components/MonacoEditor/index.hook.js

@@ -52,11 +52,11 @@ export const useMonacoEditor = (language = 'javascript') => {
                 horizontalScrollbarSize: 8
             },
             // 行号
-            tabSize: 4,
+            tabSize: 8,
             //字体大小
-            fontSize: 16,
+            fontSize: 20,
             // 字体
-            fontFamily: 'Fira Code, \'JetBrains Mono\', monospace',
+            fontFamily: 'Consolas, "Courier New", monospace',
             acceptSuggestionOnCommitCharacter: true, // 接受关于提交字符的建议
             acceptSuggestionOnEnter: 'on', // 接受输入建议 "on" | "off" | "smart"
             accessibilityPageSize: 10, // 辅助功能页面大小 Number 说明:控制编辑器中可由屏幕阅读器读出的行数。警告:这对大于默认值的数字具有性能含义。
@@ -68,7 +68,7 @@ export const useMonacoEditor = (language = 'javascript') => {
             autoIndent: 'None', // 控制编辑器在用户键入、粘贴、移动或缩进行时是否应自动调整缩进
             automaticLayout: true, // 自动布局
             codeLens: false, // 是否显示codeLens 通过 CodeLens,你可以在专注于工作的同时了解代码所发生的情况 – 而无需离开编辑器。 可以查找代码引用、代码更改、关联的 Bug、工作项、代码评审和单元测试。
-            codeLensFontFamily: '', // codeLens的字体样式
+            codeLensFontFamily: 'Consolas', // codeLens的字体样式
             codeLensFontSize: 14, // codeLens的字体大小
             colorDecorators: true, // 呈现内联色彩装饰器和颜色选择器
             comments: {
@@ -97,7 +97,8 @@ export const useMonacoEditor = (language = 'javascript') => {
             readOnly: false, // 是否为只读模式
             theme: 'vs', // vs, hc-black, or vs-dark
             lineNumbers: 'on', // 显示行号
-            lineHeight: 1.5,
+            lineHeight: 1.6,
+            letterSpacing: '0.3px', // 字间距
             fontLigatures: true,
             ...editorOption
         })

+ 4 - 0
src/components/MonacoEditor/index.js

@@ -0,0 +1,4 @@
+import MonacoEditor from './index.vue';
+import EditorWorker from './EditorWorker.vue';
+
+export { MonacoEditor, EditorWorker };

+ 98 - 0
src/components/MonacoEditor/index.vue

@@ -0,0 +1,98 @@
+<template>
+  <div ref="el" class="go-editor-area" :style="{ width, height }"></div>
+  <EditorWorker></EditorWorker>
+</template>
+
+<script setup>
+import {onMounted, watch, ref, toRaw} from 'vue'
+import {useMonacoEditor} from './index.hook'
+import {EditorWorker} from './index'
+
+const props = defineProps({
+  width: {
+    type: String,
+    default: '100%'
+  },
+  height: {
+    type: String,
+    default: '90vh'
+  },
+  language: {
+    type: String,
+    default: 'typescript'
+  },
+  preComment: {
+    type: String,
+    default: ''
+  },
+  modelValue: {
+    type: String,
+    default: ''
+  },
+  editorOptions: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+const emits = defineEmits(['blur', 'update:modelValue'])
+
+const {el, updateVal, getEditor, createEditor,addContent} = useMonacoEditor(props.language)
+
+const updateMonacoVal = (_val) => {
+  const {modelValue, preComment} = props
+  const val = preComment ? `${preComment}\n${_val || modelValue}` : _val || modelValue;
+  updateVal(val)
+}
+
+onMounted(() => {
+  const monacoEditor = createEditor(props.editorOptions)
+  monacoEditor.onDidChangeModelContent(() => {
+    emits('update:modelValue', monacoEditor.getValue())
+  })
+  monacoEditor.onDidBlurEditorText(() => {
+    emits('blur')
+  })
+  updateMonacoVal()
+
+  emits('asyncGetEditor', getEditor())
+})
+defineExpose({
+  addContent
+})
+
+watch(
+    () => props.modelValue,
+    (val) => {
+      val !== getEditor()?.getValue() && updateMonacoVal(val)
+    }
+)
+</script>
+
+<style lang="less" scoped>
+.go-editor-area {
+  position: relative;
+  border-radius: 4px;
+  overflow: hidden;
+  padding: 5px;
+  padding-left: 0;
+  box-sizing: border-box;
+  background-color: rgba(0, 0, 0, 0);
+@include deep() {
+  .margin,
+  .monaco-editor,
+  .inputarea.ime-input {
+    background-color: rgba(0, 0, 0, 0);
+  }
+
+  .monaco-editor-background {
+    background-color: rgba(0, 0, 0, 0);
+  @include fetch-bg-color('filter-color-shallow');
+  }
+
+  .margin {
+  @include fetch-bg-color('filter-color-shallow');
+  }
+}
+}
+</style>

+ 360 - 0
src/components/common/common-card/index.vue

@@ -0,0 +1,360 @@
+<template>
+    <div id="card" ref="rootRef">
+      <div class="contentmain" v-for="(item,index) in dataSource" :key="index">
+          <div class="card-left">
+            <div class="titledetail">
+              <div class="titledetail-l">
+                <div class="arrow">
+                  <span style="margin-left: 10px;">{{ item.sequenceNo || '-' }}</span>
+                </div>
+                <div class="projectname">
+                  <span class="active" v-show="item.projectType == 1">政府委托</span>
+                  <span class="active" v-show="item.projectType == 2">社会委托</span> | 
+                  <span>评估咨询</span>
+                </div>
+                <div class="flowtype">
+                  <div class="type-f">
+                    <span>委托书编号 : {{ item.delegentNumber || '-' }}</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div class="contentdetail">
+              <div class="detailodd">
+                <div style="width: 99%" >
+                  <a-descriptions :column="4">
+                    <a-descriptions-item  label="单据编号">
+                      <span>{{ item.sequenceNo || '-' }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="单据状态">
+                      <span>{{ item.instanceStatus }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="委托书编号">
+                      <span>{{ item.delegentNumber }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="承办部门">
+                      <span>{{ item.chargeDept }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="项目名称">
+                      <span>{{ item.projectName }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="服务范围">
+                      <span>{{ item.serviceScope }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="专业">
+                      <span>{{ item.spcialty }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="委托单位">
+                      <span>{{ item.entrustedUnitName }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="发起日期">
+                      <span>{{ item.createTime }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="委托书日期">
+                      <span>{{ item.letterDate }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="主要委托内容">
+                      <span>{{ item.delegentContent }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="要求完成日期">
+                      <span>{{ item.endDate }}</span>
+                    </a-descriptions-item>
+                    <a-descriptions-item  label="备注">
+                      <span>{{ item.remark }}</span>
+                    </a-descriptions-item>
+                  </a-descriptions>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="rightbtn">
+            <div class="btn-f">
+              <span @click="handleDetail(item.id)">查看详情</span>
+            </div>
+          </div>
+      </div>
+    </div>
+  </template>
+  <script setup>
+ import { ref, onMounted, onUnmounted, reactive } from 'vue';
+ import { useRouter } from 'vue-router';
+  const props = defineProps({
+    dataSource:{
+      type:Object,
+      default: () => ({}),
+    }
+  });
+  const router = useRouter();
+  const activeKey = ref(1)
+  function handleDetail(id) {
+    router.push(`/government-entrusted/project-detail?id=${id}`);
+  }
+  
+  </script>
+  <style lang="less" scoped>
+      #card {
+              width: 100%;
+              
+              // height: calc(var(--fitWidthRatio) * 200px);
+              // background: yellow;
+              .contentmain {
+                display: flex;
+                flex-flow: row;
+                align-items: center;
+                // margin: calc(var(--fitWidthRatio) * 10px) calc(var(--fitWidthRatio) * 18px);
+                border-radius: calc(var(--fitWidthRatio) * 10px);
+                border: 1px solid #dbdbdb;
+                margin-top: 20px;
+
+                .card-left {
+                  width: 85%;
+                  // height: 135px;
+                  border-radius: 10px 0 0 10px;
+                  box-shadow: 3px 0px 4px 1px #bcbcbc66;
+                  position: relative;
+
+                  .titledetail {
+                    width: 100%;
+                    height: 45px;
+                    background: linear-gradient(90deg, #ebf1ff 0.14%, #f9f9f9 75.39%);
+                    display: flex;
+                    flex-flow: row;
+                    align-items: center;
+                    justify-content: space-between;
+                    border-radius: 10px 0 0 0;
+
+                    .titledetail-l {
+                      display: flex;
+                      flex-flow: row;
+                      align-items: center;
+
+                      .projectname {
+                        font-size: calc(var(--fitWidthRatio) * 15px);
+                        .active{
+                          color: rgb(255,0 ,0);
+                          font-weight: 800;
+                        }
+                      }
+
+                      .flowtype {
+                        display: flex;
+                        flex-flow: row;
+                        position: absolute;
+                        right: 20px;
+
+                        .type-f {
+                          border-radius: 10px;
+                          background: #3a68f0;
+                          color: #fff;
+                          font-size: calc(var(--fitWidthRatio) * 13px);
+                          display: -webkit-box;
+                          display: -ms-flexbox;
+                          display: flex;
+                          align-items: center;
+                          justify-content: center;
+                          padding: 4px 8px;
+                          margin-left: 10px;
+                        }
+
+                        .type-s {
+                          border-radius: 0.5em;
+                          border: 1px solid #3a68f0;
+                          color: #3a68f0;
+                          font-size: 1.3em;
+                          display: flex;
+                          align-items: center;
+                          justify-content: center;
+                          padding: 2px 5px;
+                          margin-left: calc(var(--fitWidthRatio) * 5px);
+                        }
+
+                        .type-t {
+                          border-radius: 5px;
+                          border: 1px solid #ff6c58;
+                          color: #ff6c58;
+                          font-size: calc(var(--fitWidthRatio) * 13px);
+                          display: flex;
+                          align-items: center;
+                          justify-content: center;
+                          padding: 2px 5px;
+                          margin-left: calc(var(--fitWidthRatio) * 5px);
+                        }
+
+                        .type-t1 {
+                          border-radius: calc(var(--fitWidthRatio) * 5px);
+                          border: 1px solid #23cab6;
+                          color: #23cab6;
+                          font-size: 1.2em;
+                          display: flex;
+                          align-items: center;
+                          justify-content: center;
+                          padding: 2px 5px;
+                          margin-left: calc(var(--fitWidthRatio) * 5px);
+                        }
+                      }
+                    }
+
+                    .titledetail-r {
+                      width: calc(var(--fitWidthRatio) * 500px);
+                      height: 100%;
+                      font-size: calc(var(--fitWidthRatio) * 15px);
+                      margin-right: 10px;
+                      color: #44444f;
+                      display: flex;
+                      flex-flow: row;
+                      align-items: center;
+                      justify-content: space-between;
+                    }
+                  }
+
+                  .contentdetail {
+                    width: 99%;
+                    // height: 110px;
+                    background: #fff;
+                    display: flex;
+                    flex-flow: column;
+                    align-items: center;
+                    justify-content: center;
+                    border-radius: 0 0 0 10px;
+
+                    .detailodd {
+                      width: 100%;
+                      height: 100%;
+                      margin: 0 0 0 10px;
+                      padding: 10px 0 0 0;
+                      display: flex;
+                      flex-flow: row;
+                      align-items: center;
+                      border-radius: 10px 0 0 10px;
+                      background: #fff;
+
+                      ::v-deep .el-descriptions-row {
+                        background-color: #fff;
+                      }
+
+                      ::v-deep .el-descriptions-item__cell {
+                        padding-bottom: 4px !important;
+                      }
+
+                      .laname {
+                        font-size: calc(var(--fitWidthRatio) * 15px);
+                        width: 1%;
+                        margin: 0 15px;
+                        color: #abc4ff;
+                      }
+                    }
+
+                    .detaileven {
+                      width: 100%;
+                      height: 100%;
+                      margin: calc(var(--fitWidthRatio) * 10px) 0 0 calc(var(--fitWidthRatio) * 10px);
+                      display: flex;
+                      flex-flow: row;
+                      align-items: center;
+                      background: #f5f8ff;
+                      border-radius: 10px 0 0 10px;
+                      padding: calc(var(--fitWidthRatio) * 10px) 0;
+
+                      ::v-deep .el-descriptions-row {
+                        background-color: #f5f8ff;
+                      }
+
+                      ::v-deep .el-descriptions-item__cell {
+                        padding-bottom: 4px !important;
+                      }
+
+                      .laname {
+                        font-size: calc(var(--fitWidthRatio) * 15px);
+                        width: 1%;
+                        margin: 0 15px;
+                        color: #86909c;
+                      }
+                    }
+                  }
+                }
+
+                .rightbtn {
+                  margin: calc(var(--fitWidthRatio) * 10px);
+                  width: 15%;
+                  height: calc(var(--fitWidthRatio) * 117px);
+                  display: flex;
+                  flex-direction: column;
+                  justify-content: space-evenly;
+                  align-items: center;
+
+                  .btn-f {
+                    border-radius: 10px;
+                    background: #3a68f0;
+                    color: #fff;
+                    font-size: calc(var(--fitWidthRatio) * 15px);
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    cursor: pointer;
+                    span{
+                      padding: 4px 40px;
+                    }
+                  }
+
+                  .btn-s {
+                    border-radius: 10px;
+                    border: 1px solid #3a68f0;
+                    color: #3a68f0;
+                    font-size: calc(var(--fitWidthRatio) * 13px);
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    width: calc(var(--fitWidthRatio) * 110px);
+                    height: calc(var(--fitWidthRatio) * 35px);
+                    cursor: pointer;
+                  }
+
+                  .btn-t {
+                    border-radius: 10px;
+                    border: 1px solid #e2e2ea;
+                    color: #44444f;
+                    font-size: calc(var(--fitWidthRatio) * 13px);
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    width: calc(var(--fitWidthRatio) * 110px);
+                    height: calc(var(--fitWidthRatio) * 35px);
+                    cursor: pointer;
+                  }
+                }
+
+                ::v-deep .is-plain {
+                  background: #fff !important;
+                }
+
+                ::v-deep .is-plain:hover {
+                  color: #2b69f8 !important;
+                }
+              }
+
+              .arrow {
+                position: relative;
+                padding: calc(var(--fitWidthRatio) * 10px) calc(var(--fitWidthRatio) * 20px);
+                height: calc(var(--fitWidthRatio) * 32px);
+                background-color: #75a3ff;
+                display: flex;
+                align-items: center;
+                color: #fff;
+                font-size: calc(var(--fitWidthRatio) * 15px);
+                cursor: pointer;
+                width: 204px;
+
+                /**
+       * 箭头
+       */
+                &::after {
+                  content: '';
+                  position: absolute;
+                  right: 0%;
+                  top: 0;
+                  border: 13px solid transparent;
+                  border-right-color: #ebf1ff;
+                }
+              }
+            }
+  </style>

+ 36 - 0
src/components/common/common-drawer/index copy.vue

@@ -0,0 +1,36 @@
+<template>
+  <a-drawer
+    v-model:open="open"
+    :headerStyle="{
+      background: 'rgba(61, 127, 255, 0.2)',
+      boxShadow: '0px 2px 10px 0px rgba(217, 217, 217, 1)',
+    }"
+    :bodyStyle="{
+      background: 'linear-gradient(180deg, #DFECFF 0%, #FFFFFF 100%)',
+    }"
+    placement="right"
+    destroyOnClose
+    :keyboard="false"
+    width="88%"
+    getContainer="body"
+  >
+    <template #title>
+      <slot name="title"></slot>
+    </template>
+    <slot name="content"></slot>
+  </a-drawer>
+</template>
+<script setup lang="jsx">
+  import { ref } from 'vue';
+
+  const open = ref(false);
+
+  function showDrawer(record) {
+    open.value = true;
+  }
+
+  defineExpose({
+    showDrawer,
+  });
+</script>
+<style lang="less" scoped></style>

+ 36 - 0
src/components/common/common-drawer/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <a-drawer
+    v-model:open="open"
+    :headerStyle="{
+      background: 'rgba(61, 127, 255, 0.2)',
+      boxShadow: '0px 2px 10px 0px rgba(217, 217, 217, 1)',
+    }"
+    :bodyStyle="{
+      background: 'linear-gradient(180deg, #DFECFF 0%, #FFFFFF 100%)',
+    }"
+    placement="right"
+    destroyOnClose
+    :keyboard="false"
+    width="87%"
+    getContainer="body"
+  >
+    <template #title>
+      <slot name="title"></slot>
+    </template>
+    <slot name="content"></slot>
+  </a-drawer>
+</template>
+<script setup lang="jsx">
+  import { ref } from 'vue';
+
+  const open = ref(false);
+
+  function showDrawer(record) {
+    open.value = true;
+  }
+
+  defineExpose({
+    showDrawer,
+  });
+</script>
+<style lang="less" scoped></style>

+ 149 - 0
src/components/common/common-form/index.vue

@@ -0,0 +1,149 @@
+<template>
+    <a-form ref="formRef" :model="formData" v-if="props.form">
+        <template v-for="(item, index) in props.form" :key="item.label">
+            <!-- 输入框 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules" v-if="item.type == 'input'"
+                v-bind="{ ...item.formItemAttrs }">
+                <a-input v-model:value="formData[item.dataIndex]" :placeholder="`请输入${item.label}`"
+                    v-bind="{ ...item.operation }" />
+            </a-form-item>
+            <!-- 选择框 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'select'" v-bind="{ ...item.formItemAttrs }">
+                <a-select v-bind="{ ...item.operation }" v-model:value="formData[item.dataIndex]"
+                    :fieldNames="item.fieldNames" :options="isRefOptions(item.options)"
+                    :placeholder="`请选择${item.label}`" @change="item.onChange" />
+            </a-form-item>
+            <!-- 文本域 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'textarea'" v-bind="{ ...item.formItemAttrs }">
+                <a-textarea v-model:value="formData[item.dataIndex]" :placeholder="`请输入${item.label}`"
+                    v-bind="{ ...item.operation }" />
+            </a-form-item>
+            <!-- 数字输入框 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'inputNumber'" v-bind="{ ...item.formItemAttrs }">
+                <a-input-number v-model:value="formData[item.dataIndex]" :placeholder="`请输入${item.label}`"
+                    v-bind="{ ...item.operation }" style="width: 100%" />
+            </a-form-item>
+            <!-- 单选框 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules" v-else-if="item.type == 'radio'"
+                v-bind="{ ...item.formItemAttrs }">
+                <a-radio-group v-model:value="formData[item.dataIndex]" :options="isRefOptions(item.options)"
+                    v-bind="{ ...item.operation }" :placeholder="`请输入${item.label}`" style="width: 100%" />
+            </a-form-item>
+            <!-- 时间范围选择器 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'dateRange'" v-bind="{ ...item.formItemAttrs }">
+                <a-range-picker v-model:value="formData[item.dataIndex]"
+                    v-bind="{ valueFormat: 'YYYY-MM-DD', ...item.operation }" @change="item.onChange" @ok="item.ok" />
+            </a-form-item>
+            <!-- 树状选择器 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'treeSelect'" v-bind="{ ...item.formItemAttrs }">
+                <a-tree-select v-model:value="formData[item.dataIndex]" :treeData="isRefOptions(item.treeData)"
+                    style="width: 100%" :fieldNames="item.fieldNames"
+                    :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" v-bind="{ ...item.operation }"
+                    :placeholder="`请选择${item.label}`" allow-clear @change="item.onChange" />
+            </a-form-item>
+            <!-- 多选框 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'checkbox'" v-bind="{ ...item.formItemAttrs }">
+                <a-checkbox-group v-model:value="formData[item.dataIndex]" :name="item.dataIndex"
+                    @change="item.onChange" :options="isRefOptions(item.options)" />
+            </a-form-item>
+            <!-- 分段控制器 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'segmented'" v-bind="{ ...item.formItemAttrs }">
+                <a-segmented v-model:value="formData[item.dataIndex]" v-bind="{ ...item.operation }"
+                    :options="item.options" @change="item.onChange" />
+            </a-form-item>
+            <!-- 富文本 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'richText'" v-bind="{ ...item.formItemAttrs }">
+                <wangeEditor v-model:modelValue="formData[item.dataIndex]" />
+            </a-form-item>
+            <!-- 上传 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'upload'" v-bind="{ ...item.formItemAttrs }">
+                <a-upload v-model:file-list="formData[item.dataIndex]" v-bind="{ ...item.uploadAttrs }"
+                    :headers="{ 'x-access-token': useUserStore().getToken }" @change="item.onHandleChange"
+                    :customRequest="(file) => { customRequest(file, item.dataIndex) }">
+                    <div v-if="item.customRender">
+                        <component :is="item.customRender(item)"></component>
+                    </div>
+                    <div v-else style="display: flex;flex-direction: column;align-items: center;gap: 8px;">
+                        <loading-outlined v-if="loading"></loading-outlined>
+                        <plus-outlined v-else></plus-outlined>
+                        <div class="ant-upload-text">{{ item.uploadAttrs.uploadText ?? '上传文件' }}</div>
+                    </div>
+                </a-upload>
+            </a-form-item>
+            <!-- 自定义渲染 -->
+            <a-form-item :label="item.label" :name="item.dataIndex" :rules="item.rules"
+                v-else-if="item.type == 'customize'" v-bind="{ ...item.formItemAttrs }">
+                <component :is="item.customRender(item)"></component>
+            </a-form-item>
+        </template>
+    </a-form>
+</template>
+
+<script setup>
+import { ref, unref, isRef } from 'vue';
+import wangeEditor from '/@/components/framework/wangeditor/index.vue'
+import { useUserStore } from '/@/store/modules/system/user';
+import { message } from 'ant-design-vue';
+import { fileApi } from '/src/api/support/file-api';
+const formData = defineModel('formData')
+const props = defineProps({
+    form: {
+        type: Array,
+        default: []
+    }
+})
+const formRef = ref(null)
+/* 判断是否为ref类型的列表 */
+function isRefOptions(options) {
+    return isRef(options) ? unref(options) : options
+}
+/**
+ * todo 表单校验
+ */
+async function vaildForm() {
+    // console.log(formRef.value,"------formRef.value");
+
+    await formRef.value.validate().then((res) => {
+        // console.log(res);
+    })
+}
+/* 自定义上传 */
+const loading = ref(false)
+const fileData = new FormData();
+const fileList = ref([])
+async function customRequest(options, dataIndex) {
+    loading.value = true
+    try {
+        // console.log(options, dataIndex,"------options dataIndex");
+        fileData.append('file', options.file);
+        // console.log(formData)
+        await fileApi.uploadFile(fileData, 1).then((res) => {
+            let file = res.data;
+            file.url = file.fileUrl;
+            file.name = file.fileName;
+            formData.value[dataIndex].push(file);
+            fileData.delete('files')
+        });
+        // console.log(res,"------res");
+
+    } catch (e) {
+        loading.value = false
+        message.error(e)
+    } finally {
+        loading.value = false
+        fileList.value = []
+    }
+}
+defineExpose({ vaildForm })
+</script>
+
+<style lang="scss" scoped></style>

+ 130 - 0
src/components/common/common-table-no-page/components/search.vue

@@ -0,0 +1,130 @@
+<template>
+  <a-form class="smart-query-form">
+    <a-row class="smart-query-form-row">
+      <template v-for="(item, index) in searchConfig" :key="index">
+        <a-form-item :label="item.label" class="smart-query-form-item">
+          <template v-if="item.type === 'input'">
+            <a-input
+              :style="itemStyle(item)"
+              v-bind="{ allowClear: true, placeholder: `请输入${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]"
+              @pressEnter="onSearch"
+            />
+          </template>
+          <template v-if="item.type === 'inputNumber'">
+            <a-input-number
+              :style="itemStyle(item)"
+              v-bind="{ placeholder: `请输入${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]"
+              @pressEnter="onSearch"
+            />
+          </template>
+          <template v-if="item.type === 'radioGroup'">
+            <a-radio-group :style="itemStyle(item)" v-bind="{ buttonStyle: 'solid', ...item.attrs }" v-model:value="queryForm[item.field]">
+              <a-radio-button :value="undefined">全部</a-radio-button>
+              <a-radio-button v-for="(option, opIdx) in item.options" :key="opIdx" :value="option.value">{{ option.label }}</a-radio-button>
+            </a-radio-group>
+          </template>
+          <template v-if="item.type === 'select'">
+            <a-select
+              :style="itemStyle(item)"
+              v-bind="{ allowClear: true, placeholder: `请选择${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]"
+            >
+              <a-select-option v-for="(option, opIdx) in item.options" :key="opIdx" :value="option.value">{{ option.label }}</a-select-option>
+            </a-select>
+          </template>
+          <template v-if="item.type === 'dictSelect'">
+            <DictSelect
+              :style="itemStyle(item)"
+              v-bind="{ placeholder: `请选择${item.label}`, maxTagCount: 1, ...item.attrs }"
+              v-model:value="queryForm[item.field]"
+            />
+          </template>
+          <template v-if="item.type === 'date'">
+            <a-date-picker
+              :style="itemStyle(item)"
+              v-bind="{ placeholder: `请选择${item.label}`, valueFormat: 'YYYY-MM-DD', ...item.attrs }"
+              v-model:value="queryForm[item.field]"
+            />
+          </template>
+          <template v-if="item.type === 'dateRange'">
+            <a-range-picker :style="itemStyle(item)" v-bind="{ valueFormat: 'YYYY-MM-DD', ...item.attrs }" v-model:value="queryForm[item.field]" />
+          </template>
+          <template v-if="item.type === 'cascader'">
+            <a-cascader
+              :style="itemStyle(item)"
+              v-bind="{ placeholder: `请选择${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]"
+            />
+          </template>
+        </a-form-item>
+      </template>
+
+      <a-form-item class="smart-query-form-item smart-margin-left10">
+        <a-button-group>
+          <a-button type="primary" @click="onSearch">
+            <template #icon>
+              <SearchOutlined />
+            </template>
+            查询
+          </a-button>
+          <a-button @click="onReload">
+            <template #icon>
+              <ReloadOutlined />
+            </template>
+            重置
+          </a-button>
+        </a-button-group>
+      </a-form-item>
+    </a-row>
+  </a-form>
+</template>
+
+<script setup>
+  import { watchEffect } from 'vue';
+  import DictSelect from '/@/components/common/dict-select/index.vue';
+  const props = defineProps({
+    searchConfig: {
+      type: Array,
+      default: () => [],
+    },
+    queryForm: {
+      type: Object,
+      default: () => ({}),
+    },
+  });
+
+  // 动态设置自定义值
+  watchEffect(() => {
+    props.searchConfig.forEach((item) => {
+      if (item.customValue !== undefined) {
+        props.queryForm[item.field] = item.customValue; // 设置自定义值
+      }
+    });
+  });
+
+  const emit = defineEmits(['search', 'reload']);
+
+  for (const item of props.searchConfig) {
+    if (item.options && typeof item.options === 'function') {
+      item.options = await item.options();
+    }
+  }
+
+  const onSearch = () => {
+    emit('search');
+  };
+
+  const onReload = () => {
+    emit('reload');
+  };
+
+  function itemStyle(item) {
+    return {
+      width: item.hasOwnProperty('width') ? item.width : '240px',
+    };
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 520 - 0
src/components/common/common-table-no-page/components/table.vue

@@ -0,0 +1,520 @@
+<template>
+  <div class="common-table-item table" ref="table">
+    <!-- 查询表单 -->
+    <template v-if="props.search.length > 0">
+      <Search :searchConfig="props.search" v-model:queryForm="queryForm" @search="onSearch" @reload="onReload" />
+    </template>
+    <a-card :class="{ 'common-table-tabs-item-card': props.isTabs }" size="small" :bordered="false">
+      <!-- 表格上方 -->
+      <a-row class="smart-table-btn-block">
+        <!-- 操作按钮 -->
+        <div class="smart-table-operate-block">
+          <template v-if="props.buttons.length > 0">
+            <a-button v-for="(item, index) in props.buttons" :key="index" v-bind="btnsAttrs(item)">
+              <template #icon>
+                <component :is="$antIcons[item.iconName]" />
+              </template>
+              {{ item.label }}
+            </a-button>
+          </template>
+        </div>
+        <div class="smart-table-model-block">
+          <!-- 卡片模式 -->
+          <template v-if="props.isShowCardbtn">
+            <div class="smart-card-operate-button">
+              <span
+                class="list_tabs"
+                v-for="(item, index) in modelList"
+                :key="index"
+                :class="{ active: activeKey == item.key }"
+                @click="clickTab(item.key)"
+              >
+                <PicCenterOutlined v-show="item.key == 1" />
+                <PicLeftOutlined v-show="item.key == 2" />
+                {{ item.name }}
+              </span>
+            </div>
+          </template>
+          <!-- 工具栏 -->
+          <div class="smart-table-setting-block" v-if="props.showTableOperator">
+            <TableOperator v-model="tableConfig.columns" :tableId="props.tableId" :refresh="onSearch" />
+          </div>
+        </div>
+      </a-row>
+      <!-- 自定义顶部插槽内容 -->
+      <div v-if="tableConfig.renderTopSlot" class="smart-table-top-slot" style="margin-bottom: 6px">
+        <render-top-slot />
+      </div>
+      <!-- 表格 -->
+      <div>
+        <a-table v-if="props.checked === true"
+          :rowSelection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
+          v-bind="{ ...tableConfig, pagination: false }"
+          >
+        </a-table>
+        <div v-else>
+          <a-table 
+            v-if="activeKey == 1"
+            v-bind="{ ...tableConfig, pagination: false }"
+          >
+          </a-table>
+          <CommonCard v-if="activeKey == 2" :dataSource="tableConfig.dataSource"></CommonCard>
+        </div>
+      </div>
+      <!-- 分页 -->
+      <!-- <div class="smart-query-table-page" v-if="tableConfig.pagination === true && tableConfig.dataSource.length">
+        <a-pagination showSizeChanger showQuickJumper show-less-items :pageSizeOptions="PAGE_SIZE_OPTIONS"
+          :defaultPageSize="pagination.pageSize" v-model:current="pagination.pageNum"
+          v-model:pageSize="pagination.pageSize" :total="total" @change="fetchTableData"
+          @showSizeChange="fetchTableData" :show-total="(total) => `共${total}条`" />
+      </div> -->
+    </a-card>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import Search from './search.vue';
+  import TableOperator from '/@/components/support/table-operator/index.vue';
+  import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
+  import { commonTableApi } from '/@/api/support/common-table';
+  import { smartSentry } from '/@/lib/smart-sentry';
+  import { ref, reactive, onMounted, watch, h, computed, onUnmounted } from 'vue';
+
+  const state = reactive({
+    selectedRowKeys: [],
+    loading: false,
+  });
+  const activeKey = ref(1);
+  const modelList = ref([
+    {
+      key: 1,
+      name: '列表模式',
+    },
+    {
+      key: 2,
+      name: '卡片模式',
+    },
+  ]);
+  const clickTab = (key) => {
+    activeKey.value = key;
+  };
+  const hasSelected = computed(() => state.selectedRowKeys.length > 0);
+  const emit = defineEmits(['onSelectChange']);
+  const onSelectChange = (selectedRowKeys) => {
+    state.selectedRowKeys = selectedRowKeys;
+    emit('onSelectChange', selectedRowKeys);
+  };
+  const props = defineProps({
+    // 是否修改card样式
+    isTabs: {
+      type: Boolean,
+      default: false,
+    },
+    showTableOperator: {
+      type: Boolean,
+      default: true,
+    },
+    url: {
+      type: String,
+      default: '',
+    },
+    checked: {
+      type: Boolean,
+      default: false,
+    },
+    requestMethod: {
+      type: String,
+      default: 'POST',
+    },
+    tableId: {
+      type: Number,
+      default: 0,
+    },
+    tableAttrs: {
+      type: Object,
+      default: () => ({}),
+    },
+    search: {
+      type: Array,
+      default: () => [],
+    },
+    buttons: {
+      type: Array,
+      default: () => [],
+    },
+    isShowCardbtn: {
+      type: Boolean,
+      default: false,
+    },
+
+    // 表头数据
+    columns: {
+      type: Array,
+      default: () => [],
+    },
+    // 是否显示序号列
+    showIndexColumn: {
+      type: Boolean,
+      default: false,
+    },
+    beforeFetch: {
+      type: Function,
+    },
+    afterFetch: {
+      type: Function,
+    },
+    // 自定义顶部插槽渲染函数
+    renderTopSlot: {
+      type: Function,
+    },
+  });
+
+  // 查询表单
+  const queryForm = ref({});
+
+  // 分页数据
+  // const pagination = reactive({
+  //   pageNum: 1,
+  //   pageSize: PAGE_SIZE,
+  // });
+
+  // 数据总数
+  // const total = ref(0);
+  // table默认配置
+  const tableConfig = reactive({
+    size: 'small',
+    dataSource: [],
+    summary: {},
+    columns: props.columns ? props.columns : props.tableAttrs.columns ? props.tableAttrs.columns : [],
+    bordered: true,
+    pagination: false,
+    loading: false,
+    scroll: { x: 'max-content' },
+    ...props.tableAttrs,
+    renderTopSlot: props.renderTopSlot, // 添加 renderTopSlot 属性
+  });
+
+  // 动态渲染顶部插槽内容
+  const renderTopSlot = () => {
+    if (props.renderTopSlot) {
+      // 将 queryForm 和其他参数传递给 renderTopSlot 函数
+      return h(props.renderTopSlot, { data: tableConfig.summary });
+    }
+    return null;
+  };
+
+  const indexColumnWatch = watch(
+    () => props.showIndexColumn,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        const noIndexColumn = tableConfig.columns.every((item) => item.dataIndex !== '_index');
+
+        if (newVal === true && noIndexColumn) {
+          tableConfig.columns.unshift({
+            title: '序号',
+            dataIndex: '_index',
+            width: 60,
+            align: 'center',
+            fixed: 'left',
+          });
+        }
+
+        if (newVal === false && !noIndexColumn) {
+          tableConfig.columns = tableConfig.columns.filter((item) => item.dataIndex !== '_index');
+        }
+      }
+    },
+    { immediate: true }
+  );
+
+  onMounted(() => {
+    fetchTableData();
+    reduceRatio();
+  });
+  // 生成模拟数据
+  function generateMockData() {
+    const data = [];
+    for (let i = 0; i < 10; i++) {
+      const item = {};
+      for (let j = 0; j < props.columns.length; j++) {
+        item[props.columns[j].dataIndex] = `row${i + 1},col${j + 1}`;
+      }
+
+      if (props.showIndexColumn === true) {
+        // item._index = i + 1 + (pagination.pageNum - 1) * pagination.pageSize;
+        item._index = i + 1;
+      }
+      data.push(item);
+    }
+    return data;
+  }
+
+  const urlWatch = watch(
+    () => props.url,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        fetchTableData();
+      }
+    }
+  );
+
+  // 获取表格数据
+  async function fetchTableData() {
+    try {
+      if (!props.url) {
+        tableConfig.dataSource = generateMockData();
+        // total.value = tableConfig.dataSource.length;
+        return;
+      }
+
+      tableConfig.loading = true;
+
+      let params = {
+        ...queryForm.value,
+      };
+
+      // 处理日期范围选择 转为两个参数
+      for (const item of props.search) {
+        if (item.type === 'dateRange') {
+          if (params[item.field] && params[item.field].length > 0) {
+            params[`${item.field}Start`] = params[item.field][0];
+            params[`${item.field}End`] = params[item.field][1];
+            Reflect.deleteProperty(params, item.field);
+          }
+        }
+      }
+
+      let result = null;
+
+      // if (tableConfig.pagination) {
+      //   params.pageNum = pagination.pageNum;
+      //   params.pageSize = pagination.pageSize;
+      // }
+
+      if (props.beforeFetch) {
+        params = props.beforeFetch({ params });
+      }
+
+      const method = props.requestMethod.toUpperCase();
+      if (method === 'GET') {
+        result = await commonTableApi['queryTableListByGet'](props.url, params);
+      } else if (method === 'POST') {
+        result = await commonTableApi['queryTableListByPost'](props.url, params);
+      } else {
+        throw new Error('requestMethod is not supported');
+      }
+
+      if (props.showIndexColumn === true) {
+        result.data.forEach((item, index) => {
+          // item._index = index + 1 + (pagination.pageNum - 1) * pagination.pageSize;
+          item._index = index + 1;
+        });
+      }
+
+      if (props.afterFetch) {
+        tableConfig.dataSource = props.afterFetch({ data: result.data });
+      } else {
+        tableConfig.dataSource = result.data;
+        // tableConfig.summary = result.data.summary
+      }
+
+      // total.value = result.data.total;
+    } catch (err) {
+      smartSentry.captureError(err);
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  // 查询
+  function onSearch() {
+    // pagination.pageNum = 1;
+    fetchTableData();
+  }
+
+  // 重置
+  function onReload() {
+    queryForm.value = {};
+    // pagination.pageNum = 1;
+    fetchTableData();
+  }
+
+  // 按钮disabled属性调用
+  function btnsAttrs(item) {
+    return {
+      ...item.attrs,
+      disabled: item.attrs?.disabled?.(),
+    };
+  }
+  //------------- 添加响应式 -------------//
+  const table = ref();
+  function reduceRatio() {
+    handleResize();
+    window.addEventListener('resize', handleResize);
+  }
+  function handleResize() {
+    const ratio = window.innerWidth / 1920;
+    table.value.style.setProperty('--fitWidthRatio', ratio);
+  }
+  onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+  });
+  defineExpose({
+    onReload,
+    tableConfig,
+  });
+</script>
+<style lang="less" scoped>
+  // tableOptions为数组时,使用下面的样式
+  :deep(.common-table-tabs-item-card) {
+    box-shadow: none;
+
+    .ant-card-body {
+      padding: 0;
+    }
+  }
+
+  .smart-table-btn-block {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    // margin-top: 10px;
+
+    .smart-card-operate-button {
+      .list_tabs {
+        display: inline-block;
+        width: 106px;
+        height: 30px;
+        line-height: 30px;
+        text-align: center;
+        color: rgb(68, 68, 79);
+        border: 1px solid #378EFF;
+        cursor: pointer;
+      }
+
+      .active {
+        display: inline-block;
+        width: 106px;
+        height: 30px;
+        line-height: 30px;
+        text-align: center;
+        background-color: rgb(43, 105, 248);
+        color: #fff;
+        border: 1px solid #378EFF;
+        // border-radius:
+      }
+
+      .list_tabs:nth-child(1) {
+        border-radius: 4px 0 0 4px;
+      }
+
+      .list_tabs:nth-child(2) {
+        border-radius: 0 4px 4px 0;
+      }
+    }
+  }
+
+  .smart-table-model-block {
+    display: flex;
+    align-items: center;
+  }
+
+  .table {
+    .smart-table-top-slot {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-btn-primary),
+    :deep(.ant-btn-default) {
+      display: flex;
+      align-items: center;
+      padding: calc(var(--fitWidthRatio) * 5px) calc(var(--fitWidthRatio) * 8px) calc(var(--fitWidthRatio) * 6px) calc(var(--fitWidthRatio) * 8px);
+      // height: calc(var(--fitWidthRatio) * 35px);
+    }
+
+    :deep(.ant-btn-primary > span),
+    :deep(.ant-btn-default > span),
+    :deep(.ant-select-selection-item) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-select-selector) {
+      height: calc(var(--fitWidthRatio) * 35px);
+      padding: 0 calc(var(--fitWidthRatio) * 10px);
+    }
+
+    :deep(.ant-select-selection-placeholder) {
+      display: flex;
+      align-items: center;
+      // line-height: calc(var(--fitWidthRatio) * 32px);
+    }
+
+    :deep(.ant-input-affix-wrapper > input),
+    :deep(.ant-select-selection-placeholder) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-table-cell-fix-right-first::after),
+    :deep(.ant-table-cell-fix-right-last::after) {
+      transform: translateX(-90%);
+    }
+
+    :deep(.ant-form-item-label > label) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-tabs-nav) {
+      margin: 0;
+    }
+
+    :deep(.ant-table-cell-fix-right) {
+      box-shadow: calc(var(--fitWidthRatio) * -2px) 0 calc(var(--fitWidthRatio) * 8px) 0px rgba(5, 5, 5, 0.06);
+    }
+
+    :deep(.ant-tabs-tab-btn) {
+      font-family: PingFang SC;
+      font-weight: 500;
+      font-size: calc(var(--fitWidthRatio) * 16px);
+      color: #000000;
+    }
+
+    :deep(.ant-tabs-tab) {
+      padding: calc(var(--fitWidthRatio) * 16px) 0 calc(var(--fitWidthRatio) * 10px);
+    }
+
+    :deep(.ant-table-cell) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    :deep(.smart-query-form-item) {
+      margin-right: calc(var(--fitWidthRatio) * 30px);
+    }
+
+    :deep(.ant-tabs-nav-list) {
+      padding: 0 calc(var(--fitWidthRatio) * 10px);
+    }
+
+    :deep(.ant-pagination) {
+      height: calc(var(--fitWidthRatio) * 32px);
+      min-width: calc(var(--fitWidthRatio) * 32px);
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-pagination > li) {
+      height: calc(var(--fitWidthRatio) * 32px);
+      min-width: calc(var(--fitWidthRatio) * 32px);
+      line-height: calc(var(--fitWidthRatio) * 32px);
+    }
+
+    :deep(.ant-select-item) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-select-selection-item) {
+      line-height: calc(var(--fitWidthRatio) * 30px);
+    }
+  }
+</style>

+ 130 - 0
src/components/common/common-table-no-page/example.js

@@ -0,0 +1,130 @@
+export default {
+  /**
+   * @description: tab名称 仅在多个tab时需要
+   * @type {string}
+   */
+  name: undefined,
+
+  /**
+   * @description: 请求地址 为空展示mock数据
+   * @type {string}
+   */
+  url: undefined,
+
+  /**
+   * @description: 请求方法 GET | POST 不区分大小写
+   * @type {string}
+   * @default 'GET'
+   */
+  requestMethod: 'GET',
+
+  /**
+   * @description: 表格id 用于存储表格状态 顺序调整列宽等
+   * @type {string}
+   */
+  tableId: undefined,
+
+  /**
+   * @description: 搜索条件
+   * @type {array}
+   * @example
+   * {
+   * label: '名称',
+   * field: '字段',
+   * type: 'select',
+   * customValue: '自定义值', // 自定义值字段
+   * options: async () => { // 数组 | 方法(可异步)
+   *  const { data } = await api.xxx();
+   *  return [
+   *      ...data.map((item) => ({ label: item.名称, value: item.值 })),
+   *  ];
+   * },
+   * attrs: {
+   *  ...select 属性
+   * }
+   */
+  search: [],
+
+  /**
+   * @description: 表格按钮
+   * @type {array}
+   * @example
+   * {
+   *  label: '按钮名称',
+   *  iconName: 'antd图标名称',
+   *  attrs: {
+   *      type: 'primary',
+   *      onClick: () => {},
+   *      disabled: () => {},
+   *  }
+   * },
+   */
+  buttons: [],
+
+  /**
+   * @description: 表格列
+   * @type {array}
+   * @example
+   * {
+   *  title: '标题',
+   *  dataIndex: 'dataIndex',
+   *  width: 200,
+   *  ellipsis: true,
+   *  customRender: ({ text, record, index }) => { // <script lang="jsx"></script>
+   *      return <a>{text}</a>;
+   *  }
+   */
+  columns: [],
+
+  /**
+   * @description: 表格属性
+   * @type {object}
+   * @example
+   * {
+   *  ...antd table 属性
+   * }
+   */
+  tableAttrs: {},
+
+  /**
+   * @description: 是否显示序号列
+   * @type {boolean}
+   * @default false
+   */
+  showIndexColumn: false,
+
+  /**
+   * @description: 是否显示操作图标
+   * @type {boolean}
+   * @default true
+   */
+  showTableOperator: true,
+
+  /**
+   * @description: 请求前处理
+   * @type {Function}
+   * @param {object} params 请求参数
+   * @return {object} 处理后的请求参数 配置后必须返回数据
+   * @example
+   * beforeFetch({ params }) {
+   *  return params;
+   * }
+   */
+  beforeFetch({ params }) {
+    return params;
+  },
+
+  /**
+   * @description: 请求后处理
+   * @type {Function}
+   * @param {object} data 请求返回数据
+   * @return {object} 处理后的数据 配置后必须返回数据
+   * @example
+   * afterFetch({ data }) {
+   *  return data;
+   * }
+   */
+  afterFetch({ data }) {
+    return data;
+  },
+};

+ 90 - 0
src/components/common/common-table-no-page/index.vue

@@ -0,0 +1,90 @@
+<!--
+ * @Author: gaoyu 11111111@qq.com
+ * @Date: 2025-03-29 13:17:17
+ * @LastEditors: gaoyu 11111111@qq.com
+ * @LastEditTime: 2025-03-29 14:33:06
+ * @FilePath: \consultation_ui\src\components\common\common-table-no-page\index.vue
+ * @Description: 公共组件表格 无分页
+-->
+<template>
+  <template v-if="Array.isArray(props.tableOptions)">
+    <a-card size="small" :bordered="false">
+      <a-tabs v-model:activeKey="activeKey" destroyInactiveTabPane>
+        <a-tab-pane v-for="(item, index) in props.tableOptions" :key="index" :tab="item.name || `未命名${index + 1}`">
+          <Table v-bind="item" ref="tableTabsRef" />
+        </a-tab-pane>
+        <template v-for="(item, index) in tabsUseSlots" :key="item" #[item]>
+          <slot :name="`tabs-${item}`" v-bind="{ activeKey }" />
+        </template>
+      </a-tabs>
+    </a-card>
+  </template>
+  <template v-else>
+    <Table v-bind="props.tableOptions" ref="tableRef" @onSelectChange="onSelectChange" />
+  </template>
+</template>
+<script setup>
+  import { ref, useSlots } from 'vue';
+  import Table from './components/table.vue';
+
+  const emit = defineEmits(['onSelectChange']);
+  const onSelectChange = (selectedRowKeys) => {
+    emit('onSelectChange', selectedRowKeys);
+  };
+  const props = defineProps({
+    activeKey: {
+      type: Number,
+      default: 0,
+    },
+    tableOptions: {
+      type: [Array, Object],
+      default: () => ({
+        name: undefined,
+        url: undefined,
+        checked: false,
+        // requestMethod: 'POST',
+        tableId: undefined,
+        search: [],
+        buttons: [],
+        columns: [],
+        tableAttrs: {},
+        showIndexColumn: false,
+        showTableOperator: false,
+        beforeFetch({ params }) {
+          return params;
+        },
+        afterFetch({ data }) {
+          return data;
+        },
+      }),
+    },
+  });
+  const activeKey = ref(props.activeKey);
+  // console.log(activeKey, props.activeKey)
+  const slots = useSlots();
+  const tabsUseSlots = Object.keys(slots)
+    .filter((key) => key.startsWith('tabs-'))
+    .map((key) => key.replace('tabs-', ''));
+
+  const tableRef = ref(null);
+  const tableTabsRef = ref([]);
+
+  // 给外部页面使用的刷新方法
+  function reload() {
+    if (Array.isArray(props.tableOptions)) {
+      // destroyInactiveTabPane 为 true 时 始终是第一个tab
+      tableTabsRef.value[0].onReload();
+      // destroyInactiveTabPane 为 false 时
+      // tableTabsRef.value[activeKey.value].onReload();
+    } else {
+      tableRef.value.onReload();
+    }
+  }
+
+  defineExpose({
+    reload,
+    tableConfig: () => tableRef.value.tableConfig,
+  });
+</script>
+
+<style lang="less" scoped></style>

+ 134 - 0
src/components/common/common-table/components/search.vue

@@ -0,0 +1,134 @@
+<template>
+  <a-form class="smart-query-form">
+    <a-row class="smart-query-form-row">
+      <template v-for="(item, index) in searchConfig" :key="index">
+        <a-form-item :label="item.label" class="smart-query-form-item">
+          <template v-if="item.type === 'input'">
+            <a-input :style="itemStyle(item)"
+              v-bind="{ allowClear: true, placeholder: `请输入${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]" @pressEnter="onSearch" />
+          </template>
+          <template v-if="item.type === 'inputNumber'">
+            <a-input-number :style="itemStyle(item)" v-bind="{ placeholder: `请输入${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]" @pressEnter="onSearch" />
+          </template>
+          <template v-if="item.type === 'radioGroup'">
+            <a-radio-group :style="itemStyle(item)" v-bind="{ buttonStyle: 'solid', ...item.attrs }"
+              v-model:value="queryForm[item.field]">
+              <a-radio-button :value="undefined">全部</a-radio-button>
+              <a-radio-button v-for="(option, opIdx) in item.options" :key="opIdx" :value="option.value">{{ option.label
+              }}</a-radio-button>
+            </a-radio-group>
+          </template>
+          <template v-if="item.type === 'select'">
+            <a-select :style="itemStyle(item)"
+                      option-filter-prop="label"
+                      :options="item.options"
+              v-bind="{ allowClear: true, placeholder: `请选择${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]">
+
+            </a-select>
+          </template>
+          <template v-if="item.type === 'dictSelect'">
+            <DictSelect :style="itemStyle(item)"
+              v-bind="{ placeholder: `请选择${item.label}`, maxTagCount: 1, ...item.attrs }"
+              v-model:value="queryForm[item.field]" />
+          </template>
+          <template v-if="item.type === 'date'">
+            <a-date-picker :style="itemStyle(item)"
+              v-bind="{ placeholder: `请选择${item.label}`, valueFormat: 'YYYY-MM-DD', ...item.attrs }"
+              v-model:value="queryForm[item.field]" />
+          </template>
+          <template v-if="item.type === 'dateYear'">
+            <a-date-picker :style="itemStyle(item)"
+                           v-bind="{ placeholder: `请选择${item.label}`, valueFormat: 'YYYY', ...item.attrs }"
+                           v-model:value="queryForm[item.field]" />
+          </template>
+          <template v-if="item.type === 'dateRange'">
+            <a-range-picker :style="itemStyle(item)" v-bind="{ valueFormat: 'YYYY-MM-DD', ...item.attrs }"
+              v-model:value="queryForm[item.field]" />
+          </template>
+          <template v-if="item.type === 'cascader'">
+            <a-cascader :style="itemStyle(item)" v-bind="{ placeholder: `请选择${item.label}`, ...item.attrs }"
+              v-model:value="queryForm[item.field]" />
+          </template>
+          <template v-if="item.type === 'treeSelect'">
+            <a-tree-select v-model:value="queryForm[item.field]" :treeData="isRefOptions(item.treeData)"
+              :style="itemStyle(item)" :fieldNames="item.fieldNames"
+              :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" v-bind="{ ...item.attrs }"
+              :placeholder="`请选择${item.label}`" allow-clear />
+          </template>
+        </a-form-item>
+      </template>
+
+      <a-form-item class="smart-query-form-item smart-margin-left10">
+        <a-button-group>
+          <a-button type="primary" @click="onSearch">
+            <template #icon>
+              <SearchOutlined />
+            </template>
+            查询
+          </a-button>
+          <a-button @click="onReload">
+            <template #icon>
+              <ReloadOutlined />
+            </template>
+            重置
+          </a-button>
+        </a-button-group>
+      </a-form-item>
+    </a-row>
+  </a-form>
+</template>
+
+<script setup>
+import { watchEffect, isRef, unref } from 'vue';
+import DictSelect from '/@/components/common/dict-select/index.vue';
+const props = defineProps({
+  searchConfig: {
+    type: Array,
+    default: () => [],
+  },
+  queryForm: {
+    type: Object,
+    default: () => ({}),
+  },
+});
+
+// 动态设置自定义值
+watchEffect(() => {
+  props.searchConfig.forEach((item) => {
+    if (item.customValue !== undefined) {
+      props.queryForm[item.field] = item.customValue; // 设置自定义值
+    }
+  });
+});
+
+const emit = defineEmits(['search', 'reload']);
+
+function isRefOptions(options) {
+  return isRef(options) ? unref(options) : options
+}
+
+for (const item of props.searchConfig) {
+  if (item.options && typeof item.options === 'function') {
+    item.options = await item.options();
+  }
+}
+
+const onSearch = () => {
+  emit('search');
+};
+
+const onReload = () => {
+  emit('reload');
+};
+
+function itemStyle(item) {
+  return {
+    width: item.hasOwnProperty('width') ? item.width : '240px',
+  };
+}
+</script>
+
+<style lang="less" scoped></style>

+ 575 - 0
src/components/common/common-table/components/table.vue

@@ -0,0 +1,575 @@
+<template>
+  <div class="common-table-item table" ref="table">
+    <!-- 查询表单 -->
+    <template v-if="props.search.length > 0">
+      <Search :searchConfig="props.search" v-model:queryForm="queryForm" @search="onSearch" @reload="onReload" />
+    </template>
+    <a-card :class="{ 'common-table-tabs-item-card': props.isTabs }" size="small" :bordered="false">
+      <!-- 表格上方 -->
+      <a-row class="smart-table-btn-block">
+        <!-- 操作按钮 -->
+        <div class="smart-table-operate-block">
+          <template v-if="props.buttons.length > 0">
+            <template v-if="!props.buttonStyle.subContent">
+              <a-button v-for="(item, index) in props.buttons" :key="index" v-bind="btnsAttrs(item)">
+                <template #icon>
+                  <component :is="$antIcons[item.iconName]" />
+                </template>
+                {{ item.label }}
+              </a-button>
+            </template>
+            <div v-if="props.buttonStyle.subContent">
+              <a-dropdown>
+                <a-button class="ant-dropdown-link" @click.prevent type="primary"
+                          style="text-align: center; display: flex; justify-content: center; align-items: center;width: 80px;height: 30px;">
+                  {{ props.buttonStyle.content }}
+                  <CaretDownOutlined />
+                </a-button>
+                <template #overlay>
+                  <a-menu>
+                    <a-menu-item v-for="item in props.buttonStyle.subContent" :key="item">
+                      <a @click="dropdownClick(item.link)">{{ item.label }}</a>
+                    </a-menu-item>
+                  </a-menu>
+                </template>
+              </a-dropdown>
+            </div>
+          </template>
+        </div>
+        <div class="smart-table-model-block">
+          <!-- 卡片模式 -->
+          <template v-if="props.isShowCardbtn">
+            <div class="smart-card-operate-button">
+              <span
+                class="list_tabs"
+                v-for="(item, index) in modelList"
+                :key="index"
+                :class="{ active: activeKey == item.key }"
+                @click="clickTab(item.key)"
+              >
+                <PicCenterOutlined v-show="item.key == 1" />
+                <PicLeftOutlined v-show="item.key == 2" />
+                {{ item.name }}
+              </span>
+            </div>
+          </template>
+          <!-- 工具栏 -->
+          <div class="smart-table-setting-block" v-if="props.showTableOperator">
+            <TableOperator v-model="tableConfig.columns" :tableId="props.tableId" :refresh="onSearch" />
+          </div>
+        </div>
+      </a-row>
+      <!-- 自定义顶部插槽内容 -->
+      <div v-if="tableConfig.renderTopSlot" class="smart-table-top-slot" style="margin-bottom: 6px">
+        <render-top-slot />
+      </div>
+      <!-- 表格 -->
+      <div>
+        <a-table
+          v-if="props.checked === true && props.checkedTypeRadio"
+          :rowSelection="{ type: 'radio', selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
+          :rowKey="props.checked_key"
+          v-bind="{ ...tableConfig, pagination: false }"
+        >
+        </a-table>
+        <a-table
+          v-else-if="props.checked === true && !props.checkedTypeRadio"
+          :rowSelection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
+          :rowKey="props.checked_key"
+          v-bind="{ ...tableConfig, pagination: false }"
+        >
+        </a-table>
+        <div v-else>
+          <a-table v-if="activeKey == 1" v-bind="{ ...tableConfig, pagination: false }"> </a-table>
+          <CommonCard v-if="activeKey == 2" :dataSource="tableConfig.dataSource"></CommonCard>
+        </div>
+      </div>
+      <!-- 分页 -->
+      <div class="smart-query-table-page" v-if="tableConfig.pagination === true && tableConfig.dataSource.length">
+        <a-pagination
+          showSizeChanger
+          showQuickJumper
+          show-less-items
+          :pageSizeOptions="PAGE_SIZE_OPTIONS"
+          :defaultPageSize="pagination.pageSize"
+          v-model:current="pagination.pageNum"
+          v-model:pageSize="pagination.pageSize"
+          :total="total"
+          @change="fetchTableData"
+          @showSizeChange="fetchTableData"
+          :show-total="(total) => `共${total}条`"
+        />
+      </div>
+    </a-card>
+  </div>
+</template>
+
+<script setup lang="jsx">
+  import Search from './search.vue';
+  import TableOperator from '/@/components/support/table-operator/index.vue';
+  import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '/@/constants/common-const';
+  import { commonTableApi } from '/@/api/support/common-table';
+  import { smartSentry } from '/@/lib/smart-sentry';
+  import { ref, reactive, onMounted, watch, h, computed } from 'vue';
+  import useDict from "/@/utils/dict-util.js";
+  const state = reactive({
+    selectedRowKeys: [],
+    loading: false,
+  });
+  const activeKey = ref(1);
+  const modelList = ref([
+    {
+      key: 1,
+      name: '列表模式',
+    },
+    {
+      key: 2,
+      name: '卡片模式',
+    },
+  ]);
+  const clickTab = (key) => {
+    activeKey.value = key;
+  };
+  const hasSelected = computed(() => state.selectedRowKeys.length > 0);
+  const emit = defineEmits(['onSelectChange']);
+  const onSelectChange = (selectedRowKeys) => {
+    state.selectedRowKeys = selectedRowKeys;
+    emit('onSelectChange', selectedRowKeys);
+  };
+  const props = defineProps({
+    // 是否修改card样式
+    isTabs: {
+      type: Boolean,
+      default: false,
+    },
+    showTableOperator: {
+      type: Boolean,
+      default: true,
+    },
+    url: {
+      type: String,
+      default: '',
+    },
+    checked_key: {
+      type: String,
+      default: '',
+    },
+    checked: {
+      type: Boolean,
+      default: false,
+    },
+    checkedTypeRadio: {
+      type: Boolean,
+      default: false,
+    },
+    requestMethod: {
+      type: String,
+      default: 'POST',
+    },
+    tableId: {
+      type: Number,
+      default: null,
+    },
+    tableAttrs: {
+      type: Object,
+      default: () => ({}),
+    },
+    buttonStyle: {
+      type: Object,
+      default: () => ({}),
+    },
+    search: {
+      type: Array,
+      default: () => [],
+    },
+    buttons: {
+      type: Array,
+      default: () => [],
+    },
+    isShowCardbtn: {
+      type: Boolean,
+      default: false,
+    },
+
+    // 表头数据
+    columns: {
+      type: Array,
+      default: () => [],
+    },
+    // 是否显示序号列
+    showIndexColumn: {
+      type: Boolean,
+      default: false,
+    },
+    beforeFetch: {
+      type: Function,
+    },
+    afterFetch: {
+      type: Function,
+    },
+    // 自定义顶部插槽渲染函数
+    renderTopSlot: {
+      type: Function,
+    },
+  });
+
+  // 查询表单
+  const queryForm = ref({});
+
+  // 分页数据
+  const pagination = reactive({
+    pageNum: 1,
+    pageSize: PAGE_SIZE,
+  });
+
+  // 数据总数
+  const total = ref(0);
+  // table默认配置
+  const tableConfig = reactive({
+    size: 'small',
+    dataSource: [],
+    summary: {},
+    columns: props.columns ? props.columns : props.tableAttrs.columns ? props.tableAttrs.columns : [],
+    bordered: true,
+    pagination: true,
+    loading: false,
+    scroll: { x: 'max-content' },
+    ...props.tableAttrs,
+    renderTopSlot: props.renderTopSlot, // 添加 renderTopSlot 属性
+  });
+
+  // 动态渲染顶部插槽内容
+  const renderTopSlot = () => {
+    if (props.renderTopSlot) {
+      // 将 queryForm 和其他参数传递给 renderTopSlot 函数
+      return h(props.renderTopSlot, { data: tableConfig.summary });
+    }
+    return null;
+  };
+
+  const indexColumnWatch = watch(
+    () => props.showIndexColumn,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        const noIndexColumn = tableConfig.columns.every((item) => item.dataIndex !== '_index');
+
+        if (newVal === true && noIndexColumn) {
+          tableConfig.columns.unshift({
+            title: '序号',
+            dataIndex: '_index',
+            width: 60,
+            align: 'center',
+            fixed: 'left',
+          });
+        }
+
+        if (newVal === false && !noIndexColumn) {
+          tableConfig.columns = tableConfig.columns.filter((item) => item.dataIndex !== '_index');
+        }
+      }
+    },
+    { immediate: true }
+  );
+  onMounted(() => {
+    fetchTableData()
+  });
+
+  // 生成模拟数据
+  function generateMockData() {
+    const data = [];
+    for (let i = 0; i < 10; i++) {
+      const item = {};
+      for (let j = 0; j < props.columns.length; j++) {
+        item[props.columns[j].dataIndex] = `row${i + 1},col${j + 1}`;
+      }
+
+      if (props.showIndexColumn === true) {
+        // item._index = i + 1 + (pagination.pageNum - 1) * pagination.pageSize;
+        item._index = i + 1;
+      }
+      data.push(item);
+    }
+    return data;
+  }
+
+  const urlWatch = watch(
+    () => props.url,
+    (newVal, oldVal) => {
+      if (newVal !== oldVal) {
+        fetchTableData();
+      }
+    }
+  );
+  function dropdownClick(val){
+    window.open(val)
+  }
+  // 获取表格数据
+  async function fetchTableData() {
+    try {
+      if (!props.url) {
+        tableConfig.dataSource = generateMockData();
+        total.value = tableConfig.dataSource.length;
+        return;
+      }
+
+      tableConfig.loading = true;
+
+      let params = {
+        ...queryForm.value,
+      };
+
+      // 处理日期范围选择 转为两个参数
+      for (const item of props.search) {
+        if (item.type === 'dateRange') {
+          if (params[item.field] && params[item.field].length > 0) {
+            params[`${item.field}Start`] = params[item.field][0];
+            params[`${item.field}End`] = params[item.field][1];
+            Reflect.deleteProperty(params, item.field);
+          }
+        }
+      }
+
+      let result = null;
+
+      if (tableConfig.pagination) {
+        params.pageNum = pagination.pageNum;
+        params.pageSize = pagination.pageSize;
+      }
+
+      if (props.beforeFetch) {
+        params = props.beforeFetch({ params });
+      }
+
+      const method = props.requestMethod.toUpperCase();
+      if (method === 'GET') {
+        result = await commonTableApi['queryTableListByGet'](props.url, params);
+      } else if (method === 'POST') {
+        result = await commonTableApi['queryTableListByPost'](props.url, params);
+      } else {
+        throw new Error('requestMethod is not supported');
+      }
+
+      if (props.showIndexColumn === true) {
+        if (result.data.list) {
+          result.data.list.forEach((item, index) => {
+            // item._index = index + 1 + (pagination.pageNum - 1) * pagination.pageSize;
+            item._index = index + 1;
+          });
+        } else {
+          result.data.forEach((item, index) => {
+            // item._index = index + 1 + (pagination.pageNum - 1) * pagination.pageSize;
+            item._index = index + 1;
+          });
+        }
+      }
+
+      if (props.afterFetch) {
+        tableConfig.dataSource = props.afterFetch({ data: result.data });
+      } else {
+        if (result.data.list) {
+          tableConfig.dataSource = result.data.list;
+        } else {
+          tableConfig.dataSource = result.data;
+        }
+
+        if (result.data.summary) {
+          tableConfig.summary = result.data.summary;
+        }
+      }
+
+      total.value = result.data.total;
+    } catch (err) {
+      smartSentry.captureError(err);
+    } finally {
+      tableConfig.loading = false;
+    }
+  }
+
+  // 查询
+  function onSearch() {
+    pagination.pageNum = 1;
+    fetchTableData();
+  }
+
+  // 重置
+  function onReload() {
+    queryForm.value = {};
+    pagination.pageNum = 1;
+    state.selectedRowKeys = [];
+    fetchTableData();
+  }
+
+  // 按钮disabled属性调用
+  function btnsAttrs(item) {
+    return {
+      ...item.attrs,
+      disabled: item.attrs?.disabled?.(),
+    };
+  }
+
+  defineExpose({
+    onReload,
+    tableConfig,
+  });
+</script>
+<style lang="less" scoped>
+  // tableOptions为数组时,使用下面的样式
+  :deep(.common-table-tabs-item-card) {
+    box-shadow: none;
+
+    .ant-card-body {
+      padding: 0;
+    }
+  }
+
+  .smart-table-btn-block {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    // margin-top: 10px;
+
+    .smart-card-operate-button {
+      .list_tabs {
+        display: inline-block;
+        width: 106px;
+        height: 30px;
+        line-height: 30px;
+        text-align: center;
+        color: rgb(68, 68, 79);
+        border: 1px solid #378eff;
+        cursor: pointer;
+      }
+
+      .active {
+        display: inline-block;
+        width: 106px;
+        height: 30px;
+        line-height: 30px;
+        text-align: center;
+        background-color: rgb(43, 105, 248);
+        color: #fff;
+        border: 1px solid #378eff;
+        // border-radius:
+      }
+
+      .list_tabs:nth-child(1) {
+        border-radius: 4px 0 0 4px;
+      }
+
+      .list_tabs:nth-child(2) {
+        border-radius: 0 4px 4px 0;
+      }
+    }
+  }
+
+  .smart-table-model-block {
+    display: flex;
+    align-items: center;
+  }
+
+  .table {
+    .smart-table-top-slot {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-btn-primary),
+    :deep(.ant-btn-default) {
+      display: flex;
+      align-items: center;
+      padding: calc(var(--fitWidthRatio) * 5px) calc(var(--fitWidthRatio) * 8px) calc(var(--fitWidthRatio) * 6px) calc(var(--fitWidthRatio) * 8px);
+      // height: calc(var(--fitWidthRatio) * 35px);
+    }
+
+    :deep(.ant-btn-primary > span),
+    :deep(.ant-btn-default > span),
+    :deep(.ant-select-selection-item) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-select-selector) {
+      height: calc(var(--fitWidthRatio) * 35px);
+      padding: 0 calc(var(--fitWidthRatio) * 10px);
+    }
+
+    :deep(.ant-select-selection-placeholder) {
+      display: flex;
+      align-items: center;
+      // line-height: calc(var(--fitWidthRatio) * 32px);
+    }
+
+    :deep(.ant-input-affix-wrapper > input),
+    :deep(.ant-select-selection-placeholder) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-table-cell-fix-right-first::after),
+    :deep(.ant-table-cell-fix-right-last::after) {
+      transform: translateX(-90%);
+    }
+
+    :deep(.ant-form-item-label > label) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-tabs-nav) {
+      margin: 0;
+    }
+
+    :deep(.ant-table-cell-fix-right) {
+      box-shadow: calc(var(--fitWidthRatio) * -2px) 0 calc(var(--fitWidthRatio) * 8px) 0px rgba(5, 5, 5, 0.06);
+    }
+
+    :deep(.ant-table-cell) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+
+      //表格内标签适配
+      .ant-tag {
+        font-size: calc(var(--fitWidthRatio) * 16px);
+        line-height: calc(var(--fitWidthRatio) * 20px);
+        padding-inline: calc(var(--fitWidthRatio) * 7px);
+        margin-inline-end: calc(var(--fitWidthRatio) * 8px);
+      }
+
+      //表格内link按钮适配
+      .ant-btn-link {
+        font-size: calc(var(--fitWidthRatio) * 16px);
+      }
+
+      //表格内描述适配
+      .ant-typography {
+        font-size: calc(var(--fitWidthRatio) * 16px);
+      }
+    }
+
+    :deep(.smart-query-form-item) {
+      margin-right: calc(var(--fitWidthRatio) * 30px);
+    }
+
+    :deep(.ant-tabs-nav-list) {
+      padding: 0 calc(var(--fitWidthRatio) * 10px);
+    }
+
+    :deep(.ant-pagination) {
+      height: calc(var(--fitWidthRatio) * 32px);
+      min-width: calc(var(--fitWidthRatio) * 32px);
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-pagination > li) {
+      height: calc(var(--fitWidthRatio) * 32px);
+      min-width: calc(var(--fitWidthRatio) * 32px);
+      line-height: calc(var(--fitWidthRatio) * 32px);
+    }
+
+    :deep(.ant-select-item) {
+      font-size: calc(var(--fitWidthRatio) * 16px);
+    }
+
+    :deep(.ant-select-selection-item) {
+      line-height: calc(var(--fitWidthRatio) * 30px);
+    }
+  }
+</style>

+ 130 - 0
src/components/common/common-table/example.js

@@ -0,0 +1,130 @@
+export default {
+  /**
+   * @description: tab名称 仅在多个tab时需要
+   * @type {string}
+   */
+  name: undefined,
+
+  /**
+   * @description: 请求地址 为空展示mock数据
+   * @type {string}
+   */
+  url: undefined,
+
+  /**
+   * @description: 请求方法 GET | POST 不区分大小写
+   * @type {string}
+   * @default 'GET'
+   */
+  requestMethod: 'GET',
+
+  /**
+   * @description: 表格id 用于存储表格状态 顺序调整列宽等
+   * @type {string}
+   */
+  tableId: undefined,
+
+  /**
+   * @description: 搜索条件
+   * @type {array}
+   * @example
+   * {
+   * label: '名称',
+   * field: '字段',
+   * type: 'select',
+   * customValue: '自定义值', // 自定义值字段
+   * options: async () => { // 数组 | 方法(可异步)
+   *  const { data } = await api.xxx();
+   *  return [
+   *      ...data.map((item) => ({ label: item.名称, value: item.值 })),
+   *  ];
+   * },
+   * attrs: {
+   *  ...select 属性
+   * }
+   */
+  search: [],
+
+  /**
+   * @description: 表格按钮
+   * @type {array}
+   * @example
+   * {
+   *  label: '按钮名称',
+   *  iconName: 'antd图标名称',
+   *  attrs: {
+   *      type: 'primary',
+   *      onClick: () => {},
+   *      disabled: () => {},
+   *  }
+   * },
+   */
+  buttons: [],
+
+  /**
+   * @description: 表格列
+   * @type {array}
+   * @example
+   * {
+   *  title: '标题',
+   *  dataIndex: 'dataIndex',
+   *  width: 200,
+   *  ellipsis: true,
+   *  customRender: ({ text, record, index }) => { // <script lang="jsx"></script>
+   *      return <a>{text}</a>;
+   *  }
+   */
+  columns: [],
+
+  /**
+   * @description: 表格属性
+   * @type {object}
+   * @example
+   * {
+   *  ...antd table 属性
+   * }
+   */
+  tableAttrs: {},
+
+  /**
+   * @description: 是否显示序号列
+   * @type {boolean}
+   * @default false
+   */
+  showIndexColumn: false,
+
+  /**
+   * @description: 是否显示操作图标
+   * @type {boolean}
+   * @default true
+   */
+  showTableOperator: true,
+
+  /**
+   * @description: 请求前处理
+   * @type {Function}
+   * @param {object} params 请求参数
+   * @return {object} 处理后的请求参数 配置后必须返回数据
+   * @example
+   * beforeFetch({ params }) {
+   *  return params;
+   * }
+   */
+  beforeFetch({ params }) {
+    return params;
+  },
+
+  /**
+   * @description: 请求后处理
+   * @type {Function}
+   * @param {object} data 请求返回数据
+   * @return {object} 处理后的数据 配置后必须返回数据
+   * @example
+   * afterFetch({ data }) {
+   *  return data;
+   * }
+   */
+  afterFetch({ data }) {
+    return data;
+  },
+};

+ 135 - 0
src/components/common/common-table/index.vue

@@ -0,0 +1,135 @@
+<!--
+  * 公共组件  表格
+  *
+  * @Author:    DCCloud
+  * @Date:      2024-09-26 10:02:20
+-->
+<template>
+  <div ref="table" class="table">
+    <template v-if="Array.isArray(props.tableOptions)">
+      <!-- <a-card size="small" :bordered="false" class="cardTable"> -->
+      <a-tabs v-model:activeKey="activeKey" destroyInactiveTabPane @change="toRouter" :type="props.tabsStyle">
+        <a-tab-pane v-for="(item, index) in props.tableOptions" :key="index" :tab="item.name || `未命名${index + 1}`">
+          <Table v-bind="item" ref="tableTabsRef" @onSelectChange="onSelectChange" />
+        </a-tab-pane>
+        <template v-for="(item, index) in tabsUseSlots" :key="item" #[item]>
+          <slot :name="`tabs-${item}`" v-bind="{ activeKey }" />
+        </template>
+      </a-tabs>
+      <!-- </a-card> -->
+    </template>
+    <template v-else>
+      <Table v-bind="props.tableOptions" ref="tableRef" @onSelectChange="onSelectChange" />
+    </template>
+  </div>
+</template>
+<script setup>
+import { onMounted, onUnmounted, ref, useSlots } from 'vue';
+import Table from './components/table.vue';
+import { useRouter } from 'vue-router';
+const router = useRouter();
+const emit = defineEmits(['onSelectChange']);
+const onSelectChange = (selectedRowKeys) => {
+  emit('onSelectChange', selectedRowKeys);
+};
+const props = defineProps({
+  activeKey: {
+    type: Number,
+    default: 0
+  },
+  tabsStyle: {
+    type: String,
+    default: ''
+  },
+  tableOptions: {
+    type: [Array, Object],
+    default: () => ({
+      name: undefined,
+      url: undefined,
+      checked: false,
+      // requestMethod: 'POST',
+      tableId: undefined,
+      search: [],
+      buttons: [],
+      columns: [],
+      tableAttrs: {},
+      showIndexColumn: false,
+      showTableOperator: true,
+      beforeFetch({ params }) {
+        return params;
+      },
+      afterFetch({ data }) {
+        return data;
+      },
+      toRouter: '',
+    }),
+  },
+});
+const activeKey = ref(props.activeKey);
+// console.log(activeKey, props.activeKey)
+const slots = useSlots();
+const tabsUseSlots = Object.keys(slots)
+  .filter((key) => key.startsWith('tabs-'))
+  .map((key) => key.replace('tabs-', ''));
+
+const tableRef = ref(null);
+const tableTabsRef = ref([]);
+
+// 给外部页面使用的刷新方法
+function reload() {
+  if (Array.isArray(props.tableOptions)) {
+    // destroyInactiveTabPane 为 true 时 始终是第一个tab
+    tableTabsRef.value[0].onReload();
+    // destroyInactiveTabPane 为 false 时
+    // tableTabsRef.value[activeKey.value].onReload();
+  } else {
+    tableRef.value.onReload();
+  }
+}
+//------------- 添加响应式 -------------//
+onMounted(reduceRatio)
+const table = ref()
+function reduceRatio() {
+  handleResize();
+  window.addEventListener('resize', handleResize);
+}
+function handleResize() {
+  const ratio = window.innerWidth / 1920;
+  table.value.style.setProperty('--fitWidthRatio', ratio);
+}
+function toRouter(key) {
+  if (props.tableOptions[key].toRouter) {
+    router.push(props.tableOptions[key].toRouter);
+  }
+}
+onUnmounted(() => {
+  window.removeEventListener('resize', handleResize);
+})
+defineExpose({
+  reload,
+  tableConfig: () => tableRef.value.tableConfig,
+  activeKey
+});
+</script>
+
+<style lang="less" scoped>
+.table {
+  background-color: #fff;
+  border-radius: calc(var(--fitWidthRatio) * 8px);
+
+  :deep(.ant-tabs-tab-btn) {
+    font-family: PingFang SC;
+    font-weight: 500;
+    font-size: calc(var(--fitWidthRatio) * 16px);
+    color: #000000;
+  }
+
+  :deep(.ant-tabs-tab) {
+    padding: calc(var(--fitWidthRatio) * 16px) calc(var(--fitWidthRatio) * 10px);
+  }
+
+  :deep(.ant-tabs-nav-list) {
+    padding: 0 calc(var(--fitWidthRatio) * 10px);
+  }
+}
+</style>

+ 3 - 0
src/constants/common-const.js

@@ -12,6 +12,9 @@ export const PAGE_SIZE_OPTIONS = ['5', '10', '15', '20', '30', '40', '50', '75',
 //登录页面名字
 export const PAGE_PATH_LOGIN = '/login';
 
+//自动登录页面
+export const PAGE_PATH_AUTO_LOGIN = '/autoLogin';
+
 // 登录临时跳转页面
 export const PAGE_PATH_SSO = '/sso';
 

+ 24 - 1
src/main.js

@@ -29,15 +29,30 @@ import 'vue3-tabs-chrome/dist/vue3-tabs-chrome.css';
 import '/@/theme/index.less';
 import { localRead } from '/@/utils/local-util.js';
 import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
+import '/@/assets/iconfont/iconfont.js';
 
+// 导入样式
+import "/@/views/flow/stFormDesign/styles/form-design.less";
+// 导出本地iconfont
+import "/@/views/flow/stFormDesign/static/icons/iconfont";
+// 引入表单设计器
+import { StFormDesign, StFormMobileDesign } from "/@/views/flow/stFormDesign/packages/index.js";
+import StFormBuild from "/@/views/flow/stFormDesign/packages/StFormBuild/index.js";
 // 引入vxe-table
 import VxeUI from 'vxe-pc-ui'
 import 'vxe-pc-ui/lib/style.css'
 import VxeUITable from 'vxe-table'
 import 'vxe-table/lib/style.css'
 import '/@/components/BsUi/Render/index.js'
-
 import BsUi from "/@/components/BsUi"
+// 引入common-table
+import CommonTable from './components/common/common-table/index.vue';
+// 引入common-drawer
+import CommonDrawer from './components/common/common-drawer/index.vue';
+// 引入common-card
+import CommonCard from './components/common/common-card/index.vue';
+// 引入无分页common-table
+import CommonNoPageTable from './components/common/common-table-no-page/index.vue';
 
 
 /*
@@ -77,6 +92,14 @@ function initVue() {
   let vueApp = createApp(App);
   let app = vueApp.use(router).use(store).use(i18n).use(Antd).use(smartEnumPlugin, constantsInfo).use(privilegePlugin).use(JsonViewer);
   app.use(VxeUI).use(VxeUITable);
+  app.use(StFormDesign);
+  app.use(StFormMobileDesign);
+
+  app.use(StFormBuild);
+  app.component('CommonTable', CommonTable);
+  app.component('CommonDrawer', CommonDrawer);
+  app.component('CommonCard', CommonCard);
+  app.component('CommonNoPageTable', CommonNoPageTable);
   app.use(VantUi);
   //注入权限
   app.directive('privilege', {

+ 27 - 1
src/router/flow/index.js

@@ -6,6 +6,12 @@ export const flowRouters = [
       title: '待办任务',
       hideInMenu: true,
     },
+    children: [
+      {
+        path: 'formWork',
+        component: () => import('/@/views/flow/stFormWork/formWork.vue'),
+      },
+    ],
   },
   {
     path: '/flow',
@@ -13,6 +19,26 @@ export const flowRouters = [
     meta: {
       title: '查看流程',
       hideInMenu: true,
-    }
+    },
+    children: [
+      {
+        path: 'flowView',
+        component: () => import('/@/views/flow/stFlowView/flowView.vue'),
+      },
+    ],
+  },
+  {
+    path: '/printPreview',
+    name: 'printPreview',
+    meta: {
+      title: '报销审批表',
+      hideInMenu: true,
+    },
+    children: [
+      {
+        path: 'cover',
+        component: () => import('/src/views/flowCustomComponents/flowPrintPreview/cover.vue'),
+      },
+    ],
   },
 ];

+ 24 - 14
src/router/index.js

@@ -6,14 +6,14 @@
  */
 import nProgress from 'nprogress';
 import 'nprogress/nprogress.css';
-import { nextTick } from 'vue';
-import { createRouter, createWebHashHistory } from 'vue-router';
-import { routerArray } from './routers';
-import { PAGE_PATH_404, PAGE_PATH_LOGIN } from '/@/constants/common-const';
-import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
+import {nextTick} from 'vue';
+import {createRouter, createWebHashHistory} from 'vue-router';
+import {routerArray} from './routers';
+import {PAGE_PATH_404, PAGE_PATH_AUTO_LOGIN, PAGE_PATH_LOGIN} from '/@/constants/common-const';
+import {HOME_PAGE_NAME,HOME_PAGE_PATH} from '/@/constants/system/home-const';
 import SmartLayout from '../layout/index.vue';
-import { useUserStore } from '/@/store/modules/system/user';
-import { localClear, localRead } from '/@/utils/local-util';
+import {useUserStore} from '/@/store/modules/system/user';
+import {localClear, localRead,localSave} from '/@/utils/local-util';
 import _ from 'lodash';
 import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
 import useBsDict from "/@/utils/dict.js";
@@ -22,7 +22,7 @@ export const router = createRouter({
   history: createWebHashHistory(),
   routes: routerArray,
   strict: true,
-  scrollBehavior: () => ({ left: 0, top: 0 }),
+  scrollBehavior: () => ({left: 0, top: 0}),
 });
 
 // ----------------------- 路由加载前 -----------------------
@@ -31,10 +31,10 @@ router.beforeEach(async (to, from, next) => {
   nProgress.start();
 
   // 如果是白名单,不需要拦截,直接放行
-  if(to.meta.isWhite) {
+  if (to.meta.isWhite) {
     next();
     return;
-  } 
+  }
 
 
   // 公共页面,任何时候都可以跳转
@@ -44,20 +44,30 @@ router.beforeEach(async (to, from, next) => {
   }
 
   // 验证登录
+  if(to.query.token && to.query.isMobile){
+    localSave(LocalStorageKeyConst.USER_TOKEN, to.query.token ? to.query.token : '');
+    next();
+    return;
+  }
   const token = localRead(LocalStorageKeyConst.USER_TOKEN);
-  if (!token) {
+  if (!token && to.path != PAGE_PATH_AUTO_LOGIN) {//autoLogin页面不需要登录验证
     useUserStore().logout();
     if (to.path === PAGE_PATH_LOGIN) {
       next();
     } else {
-      next({ path: PAGE_PATH_LOGIN });
+      next({path: PAGE_PATH_LOGIN});
     }
     return;
   }
 
+  // if(!token){
+  //     next({path: HOME_PAGE_PATH});
+  //     return;
+  // }
+
   // 登录页,则跳转到首页
   if (to.path === PAGE_PATH_LOGIN) {
-    next({ path: HOME_PAGE_PATH });
+    next({path: HOME_PAGE_PATH});
     return;
   }
 
@@ -102,7 +112,7 @@ router.afterEach(() => {
 // ----------------------- 构建router对象 -----------------------
 const routerMap = new Map();
 
-export function buildRoutes (menuRouterList) {
+export function buildRoutes(menuRouterList) {
   let menuList = menuRouterList ? menuRouterList : useUserStore().getMenuRouterList || [];
   /**
    * 1、构建整个路由信息

+ 2 - 2
src/store/modules/websocket.js

@@ -1,6 +1,6 @@
 import {defineStore} from 'pinia';
 import {useUserStore} from '/@/store/modules/system/user';
-
+const webSocketUrl = import.meta.env.VITE_APP_API_WEBSOCKET;
 const userStore = useUserStore();
 export const useWebSocketStore = defineStore('websocket', {
     state: () => ({
@@ -11,7 +11,7 @@ export const useWebSocketStore = defineStore('websocket', {
     actions: {
         connect() {
             if (this.websocket == null) {
-                this.websocket = new WebSocket("ws://27.223.11.42:9100/scene");
+                this.websocket = new WebSocket(webSocketUrl);
                 this.websocket.onopen = () => {
                     const socketMap = {};
                     socketMap.userId = userStore.employeeId;