소스 검색

first commit

yangg 6 달 전
커밋
b29abd6a3d
49개의 변경된 파일7633개의 추가작업 그리고 0개의 파일을 삭제
  1. 24 0
      .gitignore
  2. 3 0
      .vscode/extensions.json
  3. 5 0
      README.md
  4. 10 0
      babel.config.js
  5. 13 0
      index.html
  6. 3345 0
      package-lock.json
  7. 30 0
      package.json
  8. 1 0
      public/vite.svg
  9. 66 0
      src/App.vue
  10. 71 0
      src/antdUse.js
  11. 36 0
      src/api/report.js
  12. 6 0
      src/app.less
  13. 1 0
      src/assets/svg/attachment.svg
  14. 1 0
      src/assets/svg/close.svg
  15. 1 0
      src/assets/svg/hot-for-ux.svg
  16. 1 0
      src/assets/svg/left.svg
  17. 0 0
      src/assets/svg/pause.svg
  18. 1 0
      src/assets/svg/play.svg
  19. 1 0
      src/assets/svg/plus.svg
  20. 1 0
      src/assets/svg/read.svg
  21. 1 0
      src/assets/svg/right.svg
  22. 1 0
      src/assets/svg/up.svg
  23. 0 0
      src/assets/svg/upload.svg
  24. 1 0
      src/assets/svg/word.svg
  25. 1 0
      src/assets/vue.svg
  26. 43 0
      src/components/HelloWorld.vue
  27. 113 0
      src/components/batchInput/index.vue
  28. 202 0
      src/components/c-menu/index.vue
  29. 99 0
      src/components/c-modal/index.vue
  30. 139 0
      src/components/c-table/index.vue
  31. 262 0
      src/components/editModel/index.vue
  32. 189 0
      src/components/largeSelect/index.vue
  33. 258 0
      src/components/largeTable/index.vue
  34. 280 0
      src/components/searchModel/index.vue
  35. 152 0
      src/layouts/basicLayout.vue
  36. 19 0
      src/layouts/pageLayout.vue
  37. 24 0
      src/main.js
  38. 10 0
      src/router/index.js
  39. 29 0
      src/router/menu.js
  40. 13 0
      src/store/index.js
  41. 28 0
      src/store/modules/app.js
  42. 79 0
      src/style.css
  43. 31 0
      src/utils/exportExcel.js
  44. 64 0
      src/utils/request.js
  45. 56 0
      src/utils/util.js
  46. 124 0
      src/views/login.vue
  47. 1711 0
      src/views/report.vue
  48. 21 0
      src/views/user.vue
  49. 66 0
      vite.config.js

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 5 - 0
README.md

@@ -0,0 +1,5 @@
+# Vue 3 + Vite
+
+This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).

+ 10 - 0
babel.config.js

@@ -0,0 +1,10 @@
+module.exports = {
+    presets: [
+      '@vue/cli-plugin-babel/preset'
+    ],
+    plugins: [
+      ["import", { "libraryName": "ant-design-vue", "libraryDirectory": "es", "style": true }],
+      "@vue/babel-plugin-jsx"
+    ]
+  }
+  

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Vite + Vue</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 3345 - 0
package-lock.json

@@ -0,0 +1,3345 @@
+{
+  "name": "my-vue-app",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "my-vue-app",
+      "version": "0.0.0",
+      "dependencies": {
+        "@ant-design/icons-vue": "^7.0.1",
+        "ant-design-vue": "^4.0.0-rc.6",
+        "axios": "^1.7.9",
+        "dayjs": "^1.11.13",
+        "less": "^4.2.1",
+        "less-loader": "^12.2.0",
+        "vue": "^3.5.13",
+        "vue-router": "^4.5.0",
+        "vuex": "^4.0.2"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^5.2.1",
+        "@vitejs/plugin-vue-jsx": "^4.1.1",
+        "babel-plugin-import": "^1.13.8",
+        "style-resources-loader": "^1.5.0",
+        "vite": "^6.0.5",
+        "vue-cli-plugin-style-resources-loader": "^0.1.5"
+      }
+    },
+    "node_modules/@ampproject/remapping": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@ant-design/colors": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-6.0.0.tgz",
+      "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
+      "dependencies": {
+        "@ctrl/tinycolor": "^3.4.0"
+      }
+    },
+    "node_modules/@ant-design/icons-svg": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
+      "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA=="
+    },
+    "node_modules/@ant-design/icons-vue": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz",
+      "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==",
+      "dependencies": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-svg": "^4.2.1"
+      },
+      "peerDependencies": {
+        "vue": ">=3.0.3"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.26.2",
+      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.26.2.tgz",
+      "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.25.9",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.26.3",
+      "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.26.3.tgz",
+      "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.26.0",
+      "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.26.0.tgz",
+      "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==",
+      "dev": true,
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.26.0",
+        "@babel/generator": "^7.26.0",
+        "@babel/helper-compilation-targets": "^7.25.9",
+        "@babel/helper-module-transforms": "^7.26.0",
+        "@babel/helpers": "^7.26.0",
+        "@babel/parser": "^7.26.0",
+        "@babel/template": "^7.25.9",
+        "@babel/traverse": "^7.25.9",
+        "@babel/types": "^7.26.0",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/core/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.26.3",
+      "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.26.3.tgz",
+      "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/parser": "^7.26.3",
+        "@babel/types": "^7.26.3",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-annotate-as-pure": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz",
+      "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz",
+      "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/compat-data": "^7.25.9",
+        "@babel/helper-validator-option": "^7.25.9",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-create-class-features-plugin": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz",
+      "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.25.9",
+        "@babel/helper-member-expression-to-functions": "^7.25.9",
+        "@babel/helper-optimise-call-expression": "^7.25.9",
+        "@babel/helper-replace-supers": "^7.25.9",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+        "@babel/traverse": "^7.25.9",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-member-expression-to-functions": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz",
+      "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/traverse": "^7.25.9",
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
+      "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/traverse": "^7.25.9",
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.26.0",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz",
+      "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.25.9",
+        "@babel/helper-validator-identifier": "^7.25.9",
+        "@babel/traverse": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-optimise-call-expression": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz",
+      "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz",
+      "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-replace-supers": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz",
+      "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-member-expression-to-functions": "^7.25.9",
+        "@babel/helper-optimise-call-expression": "^7.25.9",
+        "@babel/traverse": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz",
+      "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/traverse": "^7.25.9",
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
+      "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+      "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz",
+      "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.26.0",
+      "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.26.0.tgz",
+      "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.25.9",
+        "@babel/types": "^7.26.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.26.3",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.3.tgz",
+      "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
+      "dependencies": {
+        "@babel/types": "^7.26.3"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-jsx": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz",
+      "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-typescript": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz",
+      "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-typescript": {
+      "version": "7.26.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.3.tgz",
+      "integrity": "sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-annotate-as-pure": "^7.25.9",
+        "@babel/helper-create-class-features-plugin": "^7.25.9",
+        "@babel/helper-plugin-utils": "^7.25.9",
+        "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9",
+        "@babel/plugin-syntax-typescript": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.26.0",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.26.0.tgz",
+      "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.25.9",
+      "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.25.9.tgz",
+      "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.25.9",
+        "@babel/parser": "^7.25.9",
+        "@babel/types": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.26.4",
+      "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.26.4.tgz",
+      "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.26.2",
+        "@babel/generator": "^7.26.3",
+        "@babel/parser": "^7.26.3",
+        "@babel/template": "^7.25.9",
+        "@babel/types": "^7.26.3",
+        "debug": "^4.3.1",
+        "globals": "^11.1.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.26.3",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.26.3.tgz",
+      "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.25.9",
+        "@babel/helper-validator-identifier": "^7.25.9"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
+      "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.9.2.tgz",
+      "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="
+    },
+    "node_modules/@emotion/unitless": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmmirror.com/@emotion/unitless/-/unitless-0.8.1.tgz",
+      "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ=="
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
+      "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
+      "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
+      "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
+      "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
+      "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
+      "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
+      "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
+      "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
+      "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
+      "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
+      "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
+      "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
+      "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
+      "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
+      "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
+      "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
+      "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
+      "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
+      "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
+      "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
+      "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
+      "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
+      "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
+      "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
+      "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+      "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+      "devOptional": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.2.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "devOptional": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/set-array": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+      "devOptional": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.25",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "devOptional": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
+      "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz",
+      "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz",
+      "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz",
+      "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz",
+      "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz",
+      "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz",
+      "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz",
+      "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz",
+      "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz",
+      "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz",
+      "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz",
+      "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz",
+      "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz",
+      "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz",
+      "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz",
+      "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz",
+      "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz",
+      "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz",
+      "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@simonwep/pickr": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmmirror.com/@simonwep/pickr/-/pickr-1.8.2.tgz",
+      "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==",
+      "dependencies": {
+        "core-js": "^3.15.1",
+        "nanopop": "^2.1.0"
+      }
+    },
+    "node_modules/@types/eslint": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz",
+      "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "node_modules/@types/eslint-scope": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+      "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/eslint": "*",
+        "@types/estree": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz",
+      "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+      "devOptional": true
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "devOptional": true
+    },
+    "node_modules/@types/node": {
+      "version": "22.10.2",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.10.2.tgz",
+      "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "undici-types": "~6.20.0"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz",
+      "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==",
+      "dev": true,
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0 || ^6.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vitejs/plugin-vue-jsx": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-4.1.1.tgz",
+      "integrity": "sha512-uMJqv/7u1zz/9NbWAD3XdjaY20tKTf17XVfQ9zq4wY1BjsB/PjpJPMe2xiG39QpP4ZdhYNhm4Hvo66uJrykNLA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.26.0",
+        "@babel/plugin-transform-typescript": "^7.25.9",
+        "@vue/babel-plugin-jsx": "^1.2.5"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0 || ^6.0.0",
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/@vue/babel-helper-vue-transform-on": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.2.5.tgz",
+      "integrity": "sha512-lOz4t39ZdmU4DJAa2hwPYmKc8EsuGa2U0L9KaZaOJUt0UwQNjNA3AZTq6uEivhOKhhG1Wvy96SvYBoFmCg3uuw==",
+      "dev": true
+    },
+    "node_modules/@vue/babel-plugin-jsx": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.2.5.tgz",
+      "integrity": "sha512-zTrNmOd4939H9KsRIGmmzn3q2zvv1mjxkYZHgqHZgDrXz5B1Q3WyGEjO2f+JrmKghvl1JIRcvo63LgM1kH5zFg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.24.7",
+        "@babel/helper-plugin-utils": "^7.24.8",
+        "@babel/plugin-syntax-jsx": "^7.24.7",
+        "@babel/template": "^7.25.0",
+        "@babel/traverse": "^7.25.6",
+        "@babel/types": "^7.25.6",
+        "@vue/babel-helper-vue-transform-on": "1.2.5",
+        "@vue/babel-plugin-resolve-type": "1.2.5",
+        "html-tags": "^3.3.1",
+        "svg-tags": "^1.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/babel-plugin-resolve-type": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.2.5.tgz",
+      "integrity": "sha512-U/ibkQrf5sx0XXRnUZD1mo5F7PkpKyTbfXM3a3rC4YnUz6crHEz9Jg09jzzL6QYlXNto/9CePdOg/c87O4Nlfg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.24.7",
+        "@babel/helper-module-imports": "^7.24.7",
+        "@babel/helper-plugin-utils": "^7.24.8",
+        "@babel/parser": "^7.25.6",
+        "@vue/compiler-sfc": "^3.5.3"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
+      "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
+      "dependencies": {
+        "@babel/parser": "^7.25.3",
+        "@vue/shared": "3.5.13",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
+      "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.13",
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
+      "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
+      "dependencies": {
+        "@babel/parser": "^7.25.3",
+        "@vue/compiler-core": "3.5.13",
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.11",
+        "postcss": "^8.4.48",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
+      "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.13.tgz",
+      "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
+      "dependencies": {
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
+      "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.13",
+        "@vue/shared": "3.5.13"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
+      "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
+      "dependencies": {
+        "@vue/reactivity": "3.5.13",
+        "@vue/runtime-core": "3.5.13",
+        "@vue/shared": "3.5.13",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
+      "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.13",
+        "@vue/shared": "3.5.13"
+      },
+      "peerDependencies": {
+        "vue": "3.5.13"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.13.tgz",
+      "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
+    },
+    "node_modules/@webassemblyjs/ast": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+      "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/helper-numbers": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+      "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-api-error": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+      "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-buffer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+      "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-numbers": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+      "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+      "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/helper-wasm-section": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+      "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/wasm-gen": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/ieee754": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+      "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "node_modules/@webassemblyjs/leb128": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+      "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/utf8": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+      "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/@webassemblyjs/wasm-edit": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+      "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/helper-wasm-section": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-opt": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1",
+        "@webassemblyjs/wast-printer": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-gen": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+      "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/ieee754": "1.13.2",
+        "@webassemblyjs/leb128": "1.13.2",
+        "@webassemblyjs/utf8": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-opt": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+      "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-parser": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+      "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/ieee754": "1.13.2",
+        "@webassemblyjs/leb128": "1.13.2",
+        "@webassemblyjs/utf8": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/wast-printer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+      "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/acorn": {
+      "version": "8.14.0",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.0.tgz",
+      "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+      "devOptional": true,
+      "peer": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "devOptional": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
+      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ajv-formats/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "devOptional": true,
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/ant-design-vue": {
+      "version": "4.0.0-rc.6",
+      "resolved": "https://registry.npmmirror.com/ant-design-vue/-/ant-design-vue-4.0.0-rc.6.tgz",
+      "integrity": "sha512-j+GAhgC1p1+nmQVbaEeY2miZ1h+8jBLlxTESX93MwcshVaYTkZNhiyddtl92VvEDJTedzuX+1oT3TP5wG/+tHg==",
+      "dependencies": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-vue": "^6.1.0",
+        "@babel/runtime": "^7.10.5",
+        "@ctrl/tinycolor": "^3.5.0",
+        "@emotion/hash": "^0.9.0",
+        "@emotion/unitless": "^0.8.0",
+        "@simonwep/pickr": "~1.8.0",
+        "array-tree-filter": "^2.1.0",
+        "async-validator": "^4.0.0",
+        "csstype": "^3.1.1",
+        "dayjs": "^1.10.5",
+        "dom-align": "^1.12.1",
+        "dom-scroll-into-view": "^2.0.0",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.15",
+        "resize-observer-polyfill": "^1.5.1",
+        "scroll-into-view-if-needed": "^2.2.25",
+        "shallow-equal": "^1.0.0",
+        "stylis": "^4.1.3",
+        "throttle-debounce": "^5.0.0",
+        "vue-types": "^3.0.0",
+        "warning": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12.22.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/ant-design-vue"
+      },
+      "peerDependencies": {
+        "vue": ">=3.2.0"
+      }
+    },
+    "node_modules/ant-design-vue/node_modules/@ant-design/icons-vue": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/@ant-design/icons-vue/-/icons-vue-6.1.0.tgz",
+      "integrity": "sha512-EX6bYm56V+ZrKN7+3MT/ubDkvJ5rK/O2t380WFRflDcVFgsvl3NLH7Wxeau6R8DbrO5jWR6DSTC3B6gYFp77AA==",
+      "dependencies": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-svg": "^4.2.1"
+      },
+      "peerDependencies": {
+        "vue": ">=3.0.3"
+      }
+    },
+    "node_modules/array-tree-filter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
+      "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw=="
+    },
+    "node_modules/async-validator": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.7.9",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.9.tgz",
+      "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/babel-plugin-import": {
+      "version": "1.13.8",
+      "resolved": "https://registry.npmmirror.com/babel-plugin-import/-/babel-plugin-import-1.13.8.tgz",
+      "integrity": "sha512-36babpjra5m3gca44V6tSTomeBlPA7cHUynrE2WiQIm3rEGD9xy28MKsx5IdO45EbnpJY7Jrgd00C6Dwt/l/2Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.0.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/big.js": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.24.3",
+      "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.3.tgz",
+      "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
+      "devOptional": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001688",
+        "electron-to-chromium": "^1.5.73",
+        "node-releases": "^2.0.19",
+        "update-browserslist-db": "^1.1.1"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001690",
+      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
+      "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
+      "devOptional": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ]
+    },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/compute-scroll-into-view": {
+      "version": "1.0.20",
+      "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
+      "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg=="
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true
+    },
+    "node_modules/copy-anything": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz",
+      "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+      "dependencies": {
+        "is-what": "^3.14.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/core-js": {
+      "version": "3.39.0",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.39.0.tgz",
+      "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==",
+      "hasInstallScript": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.13",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
+      "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="
+    },
+    "node_modules/debug": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.0.tgz",
+      "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+      "dev": true,
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/dom-align": {
+      "version": "1.12.4",
+      "resolved": "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.4.tgz",
+      "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw=="
+    },
+    "node_modules/dom-scroll-into-view": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz",
+      "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w=="
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.76",
+      "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz",
+      "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==",
+      "devOptional": true
+    },
+    "node_modules/emojis-list": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz",
+      "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.18.0",
+      "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz",
+      "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/errno": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+      "optional": true,
+      "dependencies": {
+        "prr": "~1.0.1"
+      },
+      "bin": {
+        "errno": "cli.js"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
+      "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/esbuild": {
+      "version": "0.24.2",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.24.2.tgz",
+      "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.24.2",
+        "@esbuild/android-arm": "0.24.2",
+        "@esbuild/android-arm64": "0.24.2",
+        "@esbuild/android-x64": "0.24.2",
+        "@esbuild/darwin-arm64": "0.24.2",
+        "@esbuild/darwin-x64": "0.24.2",
+        "@esbuild/freebsd-arm64": "0.24.2",
+        "@esbuild/freebsd-x64": "0.24.2",
+        "@esbuild/linux-arm": "0.24.2",
+        "@esbuild/linux-arm64": "0.24.2",
+        "@esbuild/linux-ia32": "0.24.2",
+        "@esbuild/linux-loong64": "0.24.2",
+        "@esbuild/linux-mips64el": "0.24.2",
+        "@esbuild/linux-ppc64": "0.24.2",
+        "@esbuild/linux-riscv64": "0.24.2",
+        "@esbuild/linux-s390x": "0.24.2",
+        "@esbuild/linux-x64": "0.24.2",
+        "@esbuild/netbsd-arm64": "0.24.2",
+        "@esbuild/netbsd-x64": "0.24.2",
+        "@esbuild/openbsd-arm64": "0.24.2",
+        "@esbuild/openbsd-x64": "0.24.2",
+        "@esbuild/sunos-x64": "0.24.2",
+        "@esbuild/win32-arm64": "0.24.2",
+        "@esbuild/win32-ia32": "0.24.2",
+        "@esbuild/win32-x64": "0.24.2"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "devOptional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "estraverse": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esrecurse/node_modules/estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "devOptional": true
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "devOptional": true
+    },
+    "node_modules/fast-uri": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.3.tgz",
+      "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "devOptional": true
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/html-tags": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.3.1.tgz",
+      "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "optional": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/image-size": {
+      "version": "0.5.5",
+      "resolved": "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
+      "optional": true,
+      "bin": {
+        "image-size": "bin/image-size.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/is-plain-object": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-3.0.1.tgz",
+      "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-what": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz",
+      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
+    },
+    "node_modules/jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "devOptional": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/less": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmmirror.com/less/-/less-4.2.1.tgz",
+      "integrity": "sha512-CasaJidTIhWmjcqv0Uj5vccMI7pJgfD9lMkKtlnTHAdJdYK/7l8pM9tumLyJ0zhbD4KJLo/YvTj+xznQd5NBhg==",
+      "dependencies": {
+        "copy-anything": "^2.0.1",
+        "parse-node-version": "^1.0.1",
+        "tslib": "^2.3.0"
+      },
+      "bin": {
+        "lessc": "bin/lessc"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "optionalDependencies": {
+        "errno": "^0.1.1",
+        "graceful-fs": "^4.1.2",
+        "image-size": "~0.5.0",
+        "make-dir": "^2.1.0",
+        "mime": "^1.4.1",
+        "needle": "^3.1.0",
+        "source-map": "~0.6.0"
+      }
+    },
+    "node_modules/less-loader": {
+      "version": "12.2.0",
+      "resolved": "https://registry.npmmirror.com/less-loader/-/less-loader-12.2.0.tgz",
+      "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==",
+      "engines": {
+        "node": ">= 18.12.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "@rspack/core": "0.x || 1.x",
+        "less": "^3.5.0 || ^4.0.0",
+        "webpack": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@rspack/core": {
+          "optional": true
+        },
+        "webpack": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/loader-runner": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz",
+      "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=6.11.5"
+      }
+    },
+    "node_modules/loader-utils": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+      "dev": true,
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.17",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "optional": true,
+      "dependencies": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "optional": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "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
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.8",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.8.tgz",
+      "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/nanopop": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmmirror.com/nanopop/-/nanopop-2.4.2.tgz",
+      "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw=="
+    },
+    "node_modules/needle": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/needle/-/needle-3.3.1.tgz",
+      "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
+      "optional": true,
+      "dependencies": {
+        "iconv-lite": "^0.6.3",
+        "sax": "^1.2.4"
+      },
+      "bin": {
+        "needle": "bin/needle"
+      },
+      "engines": {
+        "node": ">= 4.4.x"
+      }
+    },
+    "node_modules/neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.19",
+      "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.19.tgz",
+      "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+      "devOptional": true
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/parse-node-version": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
+    },
+    "node_modules/pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "optional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.49",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.49.tgz",
+      "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+      "optional": true
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "devOptional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
+    "node_modules/rollup": {
+      "version": "4.29.1",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.29.1.tgz",
+      "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.6"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.29.1",
+        "@rollup/rollup-android-arm64": "4.29.1",
+        "@rollup/rollup-darwin-arm64": "4.29.1",
+        "@rollup/rollup-darwin-x64": "4.29.1",
+        "@rollup/rollup-freebsd-arm64": "4.29.1",
+        "@rollup/rollup-freebsd-x64": "4.29.1",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.29.1",
+        "@rollup/rollup-linux-arm-musleabihf": "4.29.1",
+        "@rollup/rollup-linux-arm64-gnu": "4.29.1",
+        "@rollup/rollup-linux-arm64-musl": "4.29.1",
+        "@rollup/rollup-linux-loongarch64-gnu": "4.29.1",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1",
+        "@rollup/rollup-linux-riscv64-gnu": "4.29.1",
+        "@rollup/rollup-linux-s390x-gnu": "4.29.1",
+        "@rollup/rollup-linux-x64-gnu": "4.29.1",
+        "@rollup/rollup-linux-x64-musl": "4.29.1",
+        "@rollup/rollup-win32-arm64-msvc": "4.29.1",
+        "@rollup/rollup-win32-ia32-msvc": "4.29.1",
+        "@rollup/rollup-win32-x64-msvc": "4.29.1",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "devOptional": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "peer": true
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "optional": true
+    },
+    "node_modules/sax": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz",
+      "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
+      "optional": true
+    },
+    "node_modules/schema-utils": {
+      "version": "2.7.1",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz",
+      "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+      "dev": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.5",
+        "ajv": "^6.12.4",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 8.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/scroll-into-view-if-needed": {
+      "version": "2.2.31",
+      "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
+      "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
+      "dependencies": {
+        "compute-scroll-into-view": "^1.0.20"
+      }
+    },
+    "node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "optional": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/shallow-equal": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/shallow-equal/-/shallow-equal-1.2.1.tgz",
+      "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "devOptional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/style-resources-loader": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/style-resources-loader/-/style-resources-loader-1.5.0.tgz",
+      "integrity": "sha512-fIfyvQ+uvXaCBGGAgfh+9v46ARQB1AWdaop2RpQw0PBVuROsTBqGvx8dj0kxwjGOAyq3vepe4AOK3M6+Q/q2jw==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.2.0",
+        "loader-utils": "^2.0.0",
+        "schema-utils": "^2.7.0",
+        "tslib": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.9"
+      },
+      "peerDependencies": {
+        "webpack": "^3.0.0 || ^4.0.0 || ^5.0.0"
+      }
+    },
+    "node_modules/stylis": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.3.4.tgz",
+      "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now=="
+    },
+    "node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/svg-tags": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/svg-tags/-/svg-tags-1.0.0.tgz",
+      "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==",
+      "dev": true
+    },
+    "node_modules/tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.37.0",
+      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.37.0.tgz",
+      "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser-webpack-plugin": {
+      "version": "5.3.11",
+      "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz",
+      "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^4.3.0",
+        "serialize-javascript": "^6.0.2",
+        "terser": "^5.31.1"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "uglify-js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.0.tgz",
+      "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/throttle-debounce": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+      "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+      "engines": {
+        "node": ">=12.22"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+    },
+    "node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "devOptional": true,
+      "peer": true
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
+      "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
+      "devOptional": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.0"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "devOptional": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "6.0.6",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-6.0.6.tgz",
+      "integrity": "sha512-NSjmUuckPmDU18bHz7QZ+bTYhRR0iA72cs2QAxCqDpafJ0S6qetco0LB3WW2OxlMHS0JmAv+yZ/R3uPmMyGTjQ==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.24.2",
+        "postcss": "^8.4.49",
+        "rollup": "^4.23.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "jiti": ">=1.21.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.5.13",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz",
+      "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.13",
+        "@vue/compiler-sfc": "3.5.13",
+        "@vue/runtime-dom": "3.5.13",
+        "@vue/server-renderer": "3.5.13",
+        "@vue/shared": "3.5.13"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-cli-plugin-style-resources-loader": {
+      "version": "0.1.5",
+      "resolved": "https://registry.npmmirror.com/vue-cli-plugin-style-resources-loader/-/vue-cli-plugin-style-resources-loader-0.1.5.tgz",
+      "integrity": "sha512-LluhjWTZmpGl3tiXg51EciF+T70IN/9t6UvfmgluJBqxbrb6OV9i7L5lTd+OKtcTeghDkhcBmYhtTxxU4w/8sQ==",
+      "dev": true
+    },
+    "node_modules/vue-router": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.0.tgz",
+      "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/vue-types": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/vue-types/-/vue-types-3.0.2.tgz",
+      "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==",
+      "dependencies": {
+        "is-plain-object": "3.0.1"
+      },
+      "engines": {
+        "node": ">=10.15.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/vuex": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/vuex/-/vuex-4.0.2.tgz",
+      "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.0.0-beta.11"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.2"
+      }
+    },
+    "node_modules/warning": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz",
+      "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+      "dependencies": {
+        "loose-envify": "^1.0.0"
+      }
+    },
+    "node_modules/watchpack": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.2.tgz",
+      "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/webpack": {
+      "version": "5.97.1",
+      "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.97.1.tgz",
+      "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/eslint-scope": "^3.7.7",
+        "@types/estree": "^1.0.6",
+        "@webassemblyjs/ast": "^1.14.1",
+        "@webassemblyjs/wasm-edit": "^1.14.1",
+        "@webassemblyjs/wasm-parser": "^1.14.1",
+        "acorn": "^8.14.0",
+        "browserslist": "^4.24.0",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.17.1",
+        "es-module-lexer": "^1.2.1",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.11",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.2.0",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^3.2.0",
+        "tapable": "^2.1.1",
+        "terser-webpack-plugin": "^5.3.10",
+        "watchpack": "^2.4.1",
+        "webpack-sources": "^3.2.3"
+      },
+      "bin": {
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-sources": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz",
+      "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+      "devOptional": true,
+      "peer": true,
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/webpack/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "devOptional": true,
+      "peer": true,
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    }
+  }
+}

+ 30 - 0
package.json

@@ -0,0 +1,30 @@
+{
+  "name": "my-vue-app",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@ant-design/icons-vue": "^7.0.1",
+    "ant-design-vue": "^4.0.0-rc.6",
+    "axios": "^1.7.9",
+    "dayjs": "^1.11.13",
+    "less": "^4.2.1",
+    "less-loader": "^12.2.0",
+    "vue": "^3.5.13",
+    "vue-router": "^4.5.0",
+    "vuex": "^4.0.2"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^5.2.1",
+    "@vitejs/plugin-vue-jsx": "^4.1.1",
+    "babel-plugin-import": "^1.13.8",
+    "style-resources-loader": "^1.5.0",
+    "vite": "^6.0.5",
+    "vue-cli-plugin-style-resources-loader": "^0.1.5"
+  }
+}

+ 1 - 0
public/vite.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

+ 66 - 0
src/App.vue

@@ -0,0 +1,66 @@
+<template>
+  <a-config-provider :locale="zhCN">
+    <div id="app">
+      <router-view />
+    </div>
+  </a-config-provider>
+</template>
+
+<script>
+import { defineComponent } from 'vue';
+import zhCN from 'ant-design-vue/es/locale/zh_CN';
+
+export default defineComponent({
+  name: 'App',
+  setup() {
+    return {
+      zhCN
+    }
+  }
+})
+</script>
+
+<style lang="less">
+@import './app.less';
+
+#app {
+  font-family: Avenir, Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+html, body {
+  margin: 0;
+  padding: 0;
+}
+/* 公共样式 */
+/* 按钮 */
+/* 新增 导入 导出 */
+#app .n-btn {
+  color: #fff;
+  background-color: @normal;
+}
+/* 编辑 */
+#app .e-btn {
+  color: #fff;
+  background-color: @edit;
+}
+/* 重置 */
+#app .w-btn {
+  color: #fff;
+  background-color: @rest;
+}
+/* 删除 */
+#app .d-btn {
+  color: #fff;
+  background-color: @delete;
+}
+/* 其他 */
+#app .o-btn {
+  color: #fff;
+  background-color: @other;
+}
+/* 按钮样式 组合 */
+#app .margin-l-10 {
+  margin-left: 10px;
+}
+</style>

+ 71 - 0
src/antdUse.js

@@ -0,0 +1,71 @@
+/* eslint-disable */
+/**
+ * 按需加载需要的antd组件
+ */
+import {
+    Layout,
+    Avatar,
+    Input,
+    InputNumber,
+    Button,
+    Radio,
+    Checkbox,
+    Select,
+    Form,
+    Row,
+    Col,
+    Modal,
+    Table,
+    Tabs,
+    Dropdown,
+    Breadcrumb,
+    Spin,
+    Menu,
+    Drawer,
+    Tooltip,
+    Tag,
+    Divider,
+    DatePicker,
+    TimePicker,
+    Upload,
+    Popconfirm,
+    message,
+    notification,
+    ConfigProvider,
+  } from "ant-design-vue";
+  
+  const antdUse = (app) => {
+    app.use(Layout);
+    app.use(Avatar);
+    app.use(Input);
+    app.use(InputNumber);
+    app.use(Button);
+    app.use(Radio);
+    app.use(Checkbox);
+    app.use(Select);
+    app.use(Form);
+    app.use(Row);
+    app.use(Col);
+    app.use(Modal);
+    app.use(Table);
+    app.use(Tabs);
+    app.use(Dropdown);
+    app.use(Breadcrumb);
+    app.use(Spin);
+    app.use(Menu);
+    app.use(Drawer);
+    app.use(Tooltip);
+    app.use(Tag);
+    app.use(Divider);
+    app.use(DatePicker);
+    app.use(TimePicker);
+    app.use(Upload);
+    app.use(Popconfirm);
+    app.use(ConfigProvider);
+    
+    app.config.globalProperties.$message = message;
+    app.config.globalProperties.$notification = notification;
+  };
+  
+  export default antdUse;
+  

+ 36 - 0
src/api/report.js

@@ -0,0 +1,36 @@
+import { axios } from '@/utils/request'
+
+export function getList (d) {
+    // return axios({
+    //     url: `/report`,
+    //     method: 'post',
+    //     data: d
+    // })
+    return new Promise(function(resolve, reject) {
+        let list = [
+            {id: 1, factoryNo: '001', factoryName: '华为', materialCode: '3Z668A', materialName: '钣金门', bomLevel: 2, version: 'N167', qty: 30},
+            {id: 2, factoryNo: '002', factoryName: '谷歌', materialCode: '3Z228A', materialName: '玻璃门', bomLevel: 3, version: 'N167', qty: 150},
+            {id: 3, factoryNo: '001', factoryName: '华为', materialCode: 'WS2452', materialName: '钣金门2', bomLevel: 1, version: 'N167', qty: 230},
+            {id: 4, factoryNo: '002', factoryName: '谷歌', materialCode: '2Z838A', materialName: '散热器', bomLevel: 2, version: 'N167', qty: 76},
+            {id: 5, factoryNo: '003', factoryName: '中芯国际', materialCode: 'QJ8623', materialName: '小J骨架', bomLevel: 2, version: 'N167', qty: 99},
+            {id: 6, factoryNo: '004', factoryName: '宁德时代', materialCode: '255FE3', materialName: '小J面板', bomLevel: 1, version: 'N167', qty: 30},
+            {id: 7, factoryNo: '004', factoryName: '宁德时代', materialCode: '90JY65', materialName: '支撑臂', bomLevel: 2, version: 'N167', qty: 52},
+            {id: 8, factoryNo: '001', factoryName: '华为', materialCode: '74GER4', materialName: '冷凝器', bomLevel: 3, version: 'N167', qty: 16},
+        ];
+        if(d.factoryNo) {
+            list = list.filter(item => item.factoryNo === d.factoryNo)
+        }
+        if(d.materialCode) {
+            list = list.filter(item => item.materialCode.indexOf(d.materialCode) > -1 )
+        }
+        resolve({
+            code: 200,
+            records: list,
+            current: 1,
+            size: 20,
+            total: 8
+        })
+    });
+}
+
+

+ 6 - 0
src/app.less

@@ -0,0 +1,6 @@
+@normal: #3F66E0;
+@edit: #00C7B8;
+@rest: #FBA908;
+@delete: #EC2424;
+@other: #09BCE4;
+@active: #00f6f0;

+ 1 - 0
src/assets/svg/attachment.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="1735280632344" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7700" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M924.672 126.976q36.864 36.864 54.784 82.432t17.92 93.696-17.92 93.696-54.784 82.432l-392.192 389.12q-36.864 36.864-90.624 61.44t-113.664 28.672-122.368-16.384-115.712-73.728q-52.224-52.224-72.704-113.152t-16.384-121.344 28.16-113.664 60.928-90.112l348.16-345.088q9.216-9.216 27.136-4.608t27.136 13.824q8.192 9.216 13.312 27.136t-4.096 27.136l-347.136 344.064q-27.648 27.648-46.08 64.512t-21.504 78.848 12.288 84.992 55.296 82.944q35.84 35.84 79.36 50.688t86.528 12.288 81.92-18.944 66.56-44.032l391.168-388.096q27.648-27.648 39.424-57.344t11.264-58.88-13.824-56.832-36.864-51.2q-44.032-43.008-98.816-40.448t-110.08 57.856l-353.28 351.232q-23.552 23.552-23.04 52.224t18.944 47.104q22.528 22.528 51.712 18.432t47.616-22.528l320.512-318.464q9.216-9.216 27.136-4.608t27.136 13.824 14.336 27.136-4.096 27.136l-321.536 318.464q-36.864 36.864-70.656 51.2t-63.488 12.8-55.296-15.872-47.104-34.816q-17.408-16.384-31.232-41.984t-15.872-56.32 10.752-65.536 49.664-70.656q18.432-18.432 32.768-33.792 12.288-13.312 23.04-23.552t11.776-11.264l285.696-284.672q36.864-36.864 80.384-57.856t88.576-24.064 88.576 12.288 80.384 52.224z" p-id="7701" fill="#1e1e1e"></path></svg>

+ 1 - 0
src/assets/svg/close.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="1735287774232" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1465" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M563.8 512l262.5-312.9c4.4-5.2 0.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9c-4.4 5.2-0.7 13.1 6.1 13.1h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z" p-id="1466"></path></svg>

+ 1 - 0
src/assets/svg/hot-for-ux.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="1735280492544" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4326" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M442.514286 73.142857c82.529524 64.24381 140.239238 126.610286 173.129143 187.099429 31.158857 57.295238 43.666286 115.907048 37.546666 175.835428l-1.219047 9.996191 6.095238-4.973715a174.055619 174.055619 0 0 0 49.249524-69.607619l2.681904-7.411809 7.704381-23.04c82.285714 55.734857 123.440762 150.064762 123.440762 283.062857C841.142857 823.515429 665.795048 950.857143 521.654857 950.857143c-144.11581 0-308.224-85.333333-334.750476-263.875048-26.550857-178.541714 83.480381-261.90019 158.427429-378.197333C395.288381 231.253333 427.690667 152.697905 442.514286 73.142857z m33.718857 154.575238c-17.554286 41.447619-39.424 82.407619-65.536 122.904381l-8.313905 12.653714c-8.411429 12.507429-17.310476 24.941714-28.818286 40.374858l-40.96 54.467047c-63.634286 86.869333-80.944762 136.021333-68.851809 217.526857 17.92 120.441905 128.341333 197.778286 257.901714 197.778286 120.905143 0 241.785905-110.933333 241.785905-249.344 0-61.976381-9.825524-111.323429-29.110857-149.699048-8.240762 9.411048-17.237333 18.285714-26.965334 26.59962l-159.085714 130.023619 26.697143-195.364572c6.41219-46.811429-2.462476-92.208762-27.648-138.483809-13.214476-24.30781-31.98781-49.737143-56.368762-76.166096l-8.338286-8.850285-6.387809 15.579428z" p-id="4327" fill="#ff4d4f"></path></svg>

+ 1 - 0
src/assets/svg/left.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="1735295375625" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3239" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M659.748571 245.272381l-51.687619-51.687619-318.439619 318.585905 318.415238 318.268952 51.712-51.736381-266.703238-266.556952z" p-id="3240"></path></svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
src/assets/svg/pause.svg


+ 1 - 0
src/assets/svg/play.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="1735378230313" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3218" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M784 371.2c-16-25.6-35.2-44.8-44.8-54.4-9.6-9.6-28.8-9.6-38.4 3.2-9.6 9.6-9.6 28.8 3.2 38.4 3.2 3.2 6.4 6.4 9.6 9.6 9.6 9.6 19.2 22.4 25.6 35.2 57.6 86.4 57.6 179.2-38.4 278.4-9.6 9.6-9.6 28.8 0 38.4 9.6 9.6 28.8 9.6 38.4 0C851.2 598.4 851.2 476.8 784 371.2z" p-id="3219"></path><path d="M896 246.4c-16-25.6-35.2-48-54.4-70.4-9.6-12.8-19.2-19.2-25.6-25.6-9.6-9.6-28.8-9.6-38.4 3.2-9.6 9.6-9.6 28.8 3.2 38.4 3.2 3.2 12.8 9.6 22.4 22.4 16 19.2 32 38.4 48 64 105.6 160 105.6 336-70.4 518.4-9.6 9.6-9.6 28.8 0 38.4 9.6 9.6 28.8 9.6 38.4 0C1014.4 630.4 1014.4 425.6 896 246.4z" p-id="3220"></path><path d="M483.2 86.4l-217.6 185.6-108.8 0c-57.6 0-108.8 48-108.8 108.8l0 272c0 60.8 48 108.8 108.8 108.8l96 0 230.4 182.4c54.4 41.6 105.6 16 105.6-51.2l0-755.2C588.8 67.2 534.4 41.6 483.2 86.4zM534.4 889.6c0 22.4-3.2 22.4-19.2 9.6l-236.8-185.6c-3.2-3.2-9.6-6.4-16-6.4l-105.6 0c-28.8 0-54.4-25.6-54.4-54.4l0-272c0-28.8 25.6-54.4 54.4-54.4l118.4 0c6.4 0 12.8-3.2 16-6.4l224-192c16-12.8 16-12.8 16 6.4L531.2 889.6z" p-id="3221"></path></svg>

+ 1 - 0
src/assets/svg/plus.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="1735280866862" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17804" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M474 152m8 0l60 0q8 0 8 8l0 704q0 8-8 8l-60 0q-8 0-8-8l0-704q0-8 8-8Z" fill="#81bdfe" p-id="17805"></path><path d="M168 474m8 0l672 0q8 0 8 8l0 60q0 8-8 8l-672 0q-8 0-8-8l0-60q0-8 8-8Z" fill="#81bdfe" p-id="17806"></path></svg>

+ 1 - 0
src/assets/svg/read.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="1735281808867" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1528" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M928 161H699.2c-49.1 0-97.1 14.1-138.4 40.7L512 233l-48.8-31.3C422 175.1 373.9 161 324.8 161H96c-17.7 0-32 14.3-32 32v568c0 17.7 14.3 32 32 32h228.8c49.1 0 97.1 14.1 138.4 40.7l44.4 28.6c1.3 0.8 2.8 1.3 4.3 1.3s3-0.4 4.3-1.3l44.4-28.6C602 807.1 650.1 793 699.2 793H928c17.7 0 32-14.3 32-32V193c0-17.7-14.3-32-32-32zM324.8 721H136V233h188.8c35.4 0 69.8 10.1 99.5 29.2l48.8 31.3 6.9 4.5v462c-47.6-25.6-100.8-39-155.2-39z m563.2 0H699.2c-54.4 0-107.6 13.4-155.2 39V298l6.9-4.5 48.8-31.3c29.7-19.1 64.1-29.2 99.5-29.2H888v488z" p-id="1529" fill="#1890ff"></path><path d="M396.9 361H211.1c-3.9 0-7.1 3.4-7.1 7.5v45c0 4.1 3.2 7.5 7.1 7.5h185.7c3.9 0 7.1-3.4 7.1-7.5v-45c0.1-4.1-3.1-7.5-7-7.5zM620 368.5v45c0 4.1 3.2 7.5 7.1 7.5h185.7c3.9 0 7.1-3.4 7.1-7.5v-45c0-4.1-3.2-7.5-7.1-7.5H627.1c-3.9 0-7.1 3.4-7.1 7.5zM396.9 501H211.1c-3.9 0-7.1 3.4-7.1 7.5v45c0 4.1 3.2 7.5 7.1 7.5h185.7c3.9 0 7.1-3.4 7.1-7.5v-45c0.1-4.1-3.1-7.5-7-7.5zM812.9 501H627.1c-3.9 0-7.1 3.4-7.1 7.5v45c0 4.1 3.2 7.5 7.1 7.5h185.7c3.9 0 7.1-3.4 7.1-7.5v-45c0.1-4.1-3.1-7.5-7-7.5z" p-id="1530" fill="#1890ff"></path></svg>

+ 1 - 0
src/assets/svg/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="1735295357873" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2254" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M605.086476 512.146286L338.358857 245.272381l51.760762-51.687619 318.415238 318.585905L390.095238 830.415238l-51.687619-51.736381z" p-id="2255"></path></svg>

+ 1 - 0
src/assets/svg/up.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="1735280808939" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15804" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M873.8816 150.1184C973.9264 250.2656 1024 370.8928 1024 512c0 141.2096-50.0736 261.8368-150.1184 361.8816C773.7344 973.9264 653.1072 1024 512 1024c-141.1072 0-261.7344-50.0736-361.8816-150.1184C50.0736 773.8368 0 653.2096 0 512c0-141.1072 50.0736-261.7344 150.1184-361.8816C250.2656 50.0736 370.8928 0 512 0 653.1072 0 773.7344 50.0736 873.8816 150.1184zM819.712 526.7456 507.0848 211.6608 192 526.7456l56.6272 54.1696 219.0336-219.0336 0 477.4912 78.7456 0L546.4064 361.8816l219.0336 219.0336L819.712 526.7456z" p-id="15805" fill="#4096ff"></path></svg>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
src/assets/svg/upload.svg


+ 1 - 0
src/assets/svg/word.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="1735287324782" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1522" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M616.746667 85.333333L896 347.904V853.333333a85.333333 85.333333 0 0 1-85.333333 85.333334H213.333333a85.333333 85.333333 0 0 1-85.333333-85.333334V170.666667a85.333333 85.333333 0 0 1 85.333333-85.333334h403.413334z m244.352 295.381334L581.802667 118.186667v177.237333a85.333333 85.333333 0 0 0 85.333333 85.333333h193.962667z m-107.264 76.373333h-49.152l-79.488 227.157333-109.354667-227.114666h-13.226667l-110.506666 226.688-77.824-226.688H265.130667l113.749333 328.192h13.226667l117.12-239.36 114.858666 239.36h13.781334l115.968-328.192z" fill="#5197e8" p-id="1523"></path></svg>

+ 1 - 0
src/assets/vue.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

+ 43 - 0
src/components/HelloWorld.vue

@@ -0,0 +1,43 @@
+<script setup>
+import { ref } from 'vue'
+
+defineProps({
+  msg: String,
+})
+
+const count = ref(0)
+</script>
+
+<template>
+  <h1>{{ msg }}</h1>
+
+  <div class="card">
+    <button type="button" @click="count++">count is {{ count }}</button>
+    <p>
+      Edit
+      <code>components/HelloWorld.vue</code> to test HMR
+    </p>
+  </div>
+
+  <p>
+    Check out
+    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
+      >create-vue</a
+    >, the official Vue + Vite starter
+  </p>
+  <p>
+    Learn more about IDE Support for Vue in the
+    <a
+      href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
+      target="_blank"
+      >Vue Docs Scaling up Guide</a
+    >.
+  </p>
+  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
+</template>
+
+<style scoped>
+.read-the-docs {
+  color: #888;
+}
+</style>

+ 113 - 0
src/components/batchInput/index.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="c-batchInput">
+    <a-input
+      style="width: 100%"
+      v-model:value="input"
+      :placeholder="placeholder"
+      @change="inputChange"
+    >
+      <template #addonAfter>
+        <CopyOutlined style="cursor: pointer" @click="showModal" />
+      </template>
+    </a-input>
+
+    <a-modal
+      :visible="visible"
+      :title="title"
+      ok-text=" 确认"
+      width="70%"
+      cancel-text="取消"
+      @ok="handleOk"
+      @cancel="handleCancel"
+    >
+      <a-textarea class="batch-input-text" v-model:value="textarea" allow-clear :rows="16" @change="textChange" />
+    </a-modal>
+  </div>
+</template>
+
+<script>
+// 一个可以批量输入的组件,换行自动切换数据,输出逗号分隔的字符串
+import { defineComponent, ref } from 'vue';
+import { CopyOutlined } from '@ant-design/icons-vue';
+
+export default defineComponent({
+  name: 'batchInput',
+  components:{
+    CopyOutlined
+  },
+  props:{
+    title: {  // 标题
+      type: String,
+      default: ''
+    },
+    value: { // 值
+      type: String,
+    },
+    placeholder: { // placeholder
+      type: String,
+      default: ''
+    },
+    change: {   //  值修改的回调
+      type: Function
+    },
+  },
+  setup(props, { emit }) {
+    const visible = ref(false);     // 显示弹窗
+    const input = ref(props.value); // 输入框值
+    const textarea = ref('');       // 文本框值
+
+    //  展示弹窗
+    const showModal =() => {
+      const reg = new RegExp(",","g");
+      textarea.value = (input.value || '').replace(reg, '\n')
+      visible.value = true
+    }
+    // 文本域 回调
+    const textChange = (e) => {
+      textarea.value = e.target.value
+    }
+    // 输入框 回调
+    const inputChange = (e) => {
+      e.preventDefault();
+      input.value = e.target.value;
+      emit('change', e);
+    }
+    // 弹窗取消
+    const handleCancel = () => {
+      visible.value = false
+    }
+    // 弹窗确定
+    const handleOk = () => {
+      const reg = new RegExp("[\r\n]","g");
+      let newValue = (textarea.value || '').replace(reg, ',')
+      if(newValue[newValue.length- 1] === ',') {
+        newValue = newValue.slice(0, newValue.length - 1)
+      }
+      input.value = newValue
+      visible.value = false
+      // 返回默认监听的数据类型
+      emit('change', {target: {value: newValue}})
+    }
+
+    return {
+      visible,
+      input,
+      textarea,
+      showModal,
+      textChange,
+      inputChange,
+      handleCancel,
+      handleOk
+    }
+  }
+})
+</script>
+<style lang="less" scoped>
+.c-batchInput {
+  width: 100%;
+}
+.batch-input-text {
+    height: 300px;
+    margin: 20px auto 0 auto;
+  }
+</style>

+ 202 - 0
src/components/c-menu/index.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="c-menu" :style="{ width: width }">
+    <div class="header">XXS</div>
+    <div class="contain">
+      <!-- 一级菜单 -->
+      <div 
+        class="m1"
+        v-for="m1 in list"
+        :key="m1.name"
+      >
+        <div @click="(e) => handleClick(e, 0, m1)">
+          <div :class="{'row': true, 'row1': true, 'actLine': actLine(m1), 'active': row(m1)}">
+            {{m1.title}}
+            <RightOutlined class="open" v-if="icon(m1, 'down')" />
+            <DownOutlined class="open" v-if="icon(m1, 'right')" />
+          </div>
+          <!-- 二级菜单 -->
+          <div :class="{'item': true, 'active': children(m1)}">
+            <div 
+              v-for="m2 in m1.children"
+              :key="m2.name"
+            >
+              <div @click="(e) => handleClick(e, 1, m2)">
+                <div :class="{'row': true, 'row2': true, 'active': row(m2)}">
+                  {{m2.title}}
+                  <RightOutlined class="open" v-if="icon(m2, 'down')" />
+                  <DownOutlined class="open" v-if="icon(m2, 'right')" />
+                </div>
+                <!-- 三级菜单 -->
+                <div :class="{'item': true, 'active': children(m2)}">
+                  <div 
+                    v-for="m3 in m2.children"
+                    :key="m3.name"
+                  >
+                    <div @click="(e) => handleClick(e, 2, m3)">
+                      <div :class="{'row': true, 'row3': true, 'active': row(m3)}">{{m3.title}}</div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref, watch } from 'vue';
+import { useRouter } from 'vue-router';
+import { RightOutlined, DownOutlined } from '@ant-design/icons-vue';
+
+export default defineComponent({
+  name: 'c-menu',
+  components: {
+    RightOutlined,
+    DownOutlined
+  },
+  props:{
+    width: {     // 宽度
+      type: String,
+      default: '250px'
+    },
+    list: {     // 菜单列表
+      type: Array,
+      default: []
+    },
+    active: {   // 当前激活菜  例: edit
+      type: String,
+      default: ''
+    },
+    actives: {   // 当前激活菜单链  例 setting-user-edit
+      type: String,
+      default: ''
+    },
+    change: {   // 菜单变化的回调
+      type: Function
+    },
+  },
+  setup(props, { emit }) {
+    const router = useRouter();
+    const activeKeys = ref(props.actives || ''); // 当前激活菜单链
+    const activeKey = ref(props.active || '');  // 当前激活菜单
+
+    // icon激活状态
+    const icon = (d, t) => {
+      if(d.children && d.children.length) {
+        let type = activeKeys.value.indexOf(d.name) >= 0 ? 'right' : 'down';
+        return type === t
+      } else {
+        return false
+      }
+    };
+    // 有子菜单的一级菜单。加一个竖线
+    const actLine = (d) => {
+      return activeKeys.value.indexOf(d.name) >= 0
+    };
+    // 子菜单菜单激活状态
+    const children = (d) => {
+      return d.children && d.children.length && (activeKeys.value.indexOf(d.name) >= 0)
+    };
+    // 菜单激活状态
+    const row = (d) => {
+      return activeKey.value === d.name
+    };
+    // 菜单点击
+    const handleClick = (e, idx, d) => {
+      e.stopPropagation(); 
+      let arr = activeKeys.value.split('-');
+      let flag = arr.some(item => item === d.name);
+      // 已存在
+      if(flag) {
+        if(idx === 0) {
+          arr = []
+        } else if(idx === 1) {
+          arr[1] = ''
+          arr[2] = ''
+        }
+      } else {
+        arr[idx] = d.name
+      }
+      activeKeys.value = arr.join('-')
+      // 没有子菜单,需跳转页面
+      if(!(d.children && d.children.length)) {
+        activeKey.value = d.name
+        router.push(d.path)
+        emit('change', d)
+      }
+    };
+
+    return {
+      activeKeys,
+      activeKey,
+      icon,
+      actLine,
+      children,
+      row,
+      handleClick
+    }
+  }
+})
+</script>
+
+<style lang="less" scoped>
+  .c-menu {
+    height: 100vh;
+    background-color: #4065e0;
+    color: #fff;
+    font-size: 16px;
+    .header {
+      font-size: 50px;
+      text-align: center;
+      line-height: 70px;
+    }
+    .contain {
+      height: calc(100vh - 70px);
+      overflow: auto;
+      .row {
+        padding: 14px 5px 14px 10px;
+        cursor: pointer;
+        position: relative;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        word-break: break-all;
+        .open {
+          font-size: 13px;
+          position: absolute;
+          top: 20px;
+          right: 10px;
+          z-index: 9;
+        }
+        &:hover {
+          color: @active;
+          background-color: #2546B1;
+        }
+      }
+      .actLine {
+        border-left: 2px solid #00f6f0;
+      }
+      .row2 {
+        padding-left: 20px;
+      }
+      .row3 {
+        padding-left: 30px;
+      }
+      .row.active {
+        color: #00F6F0;
+        background-color: #2546B1;
+      }
+      .item {
+        height: 0px;
+        overflow: hidden;
+      }
+      .item.active {
+        height: auto;
+        background-color: #2C4EBE;
+      }
+    }
+  }
+</style>

+ 99 - 0
src/components/c-modal/index.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="c-modal" v-show="visible">
+    <div 
+      class="u-container"
+      :style="{ ...styles }"
+    >
+      <div class="u-header">
+        <CloseOutlined class="u-close" @click="handleClose" />
+        {{title}}
+      </div>
+      <div class="u-body">
+        <slot style="width: 100%; height: 100%" name='content'></slot>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+// 一个显示隐藏的modal, 没有foorter, 也不销毁dom
+import { defineComponent } from 'vue';
+import { CloseOutlined } from '@ant-design/icons-vue';
+
+export default defineComponent({
+  name: 'c-modal',
+  components: {
+    CloseOutlined,
+  },
+  props:{
+    visible: {  // 显示隐藏
+      type: Boolean,
+      default: false
+    },
+    title: {
+      type: String,
+      default: ''
+    },
+    styles: {
+      type: Object,
+      default: () => {
+        return {
+          top: '25px',
+          width: '95%',
+          marginLeft: '-47.5%'
+        }
+      }
+    },
+    onClose: {
+      type: Function,
+      default: () => {}
+    },
+  },
+  setup(props, { emit }) {
+    // 关闭弹窗
+    const handleClose = () => {
+      emit('onClose')
+    }
+
+    return {
+      handleClose
+    }
+  }
+})
+</script>
+<style lang="less" scoped>
+  .c-modal {
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+    background-color: rgba(0, 0, 0, 0.6);
+    z-index: 999;
+    .u-container {
+      position: absolute;
+      left: 50%;
+      background-color: white;
+      overflow: hidden;
+      border-radius: 5px;
+    }
+    .u-header {
+      position: relative;
+      padding: 15px;
+      min-height: 50px;
+      font-weight: bold;
+      font-size: 18px;
+      border-bottom: 1px solid #C4C4C4;
+      .u-close {
+        position: absolute;
+        right: 20px;
+        top: 19px;
+        cursor: pointer;
+      }
+    }
+    .u-body {
+      min-height: 300px;
+      padding: 20px;
+    }
+  }
+</style>

+ 139 - 0
src/components/c-table/index.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="c-table" >
+    <a-table
+      :loading="loading"
+      :columns="columns"
+      :row-key="record => record[rowKey]"
+      :rowClassName="(row, idx) => { return idx % 2 === 1? 'dark-row' : 'light-row'}"
+      :data-source="dataSource"
+      :row-selection="selected ? { selectedRowKeys: selectedRowKeys, onChange: handleSelect, onSelectAll: handleSelectAll } : null"
+      @change="handleTableChange"
+      :pagination="!!pagination ? {showSizeChanger: true, showQuickJumper: true, pageSizeOptions: ['20', '50', '100', '200', '500', '1000'], ...pagination} : false"
+      :scroll="{x: scrollX || true, y: totalHeight - scrollY}"
+    >
+    </a-table>
+
+    <span class="totalTip" :style="{top: !!pagination ? '-27px' : '6px'}">共 {{total}} 条数据</span>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref } from 'vue';
+import { useStore } from 'vuex';
+
+export default defineComponent({
+  name: 'c-table',
+  props: {
+    loading: {
+      type: Boolean,
+      default: false
+    },
+    dataSource: {
+      type: Array,
+      default: []
+    },
+    rowKey: {
+      type: String,
+      default: "id"
+    },
+    columns: {
+      type: Array,
+      default: []
+    },
+    pagination: {
+      default: {
+        current: 1,
+        pageSize: 20,
+        total: 0,
+        showSizeChanger: true,
+        showQuickJumper: true,
+        pageSizeOptions: ["20", "50", "100", "200", "500", "1000"]
+      }
+    },
+    selected: { // 是否可选
+      type: Boolean,
+      default: false
+    },
+    scrollX: {
+      type: Number,
+    },
+    scrollY: {
+      type: Number,
+      default: 300
+    },
+    selectChange: { // 可选的回调
+      type: Function,
+    }, 
+    tableChange: { // 分页、筛选、排序的回调
+      type: Function,
+    },
+    selectedRow: { // 分页、筛选、排序的回调
+      type: Array,
+      default: () => { return [] }
+    },
+    total: { 
+      type: Number,
+      default: 0
+    },
+  },
+  setup(props, { emit }) {
+    const store = useStore();
+    const selectedRowKeys = ref(props.selectedRow || []);
+    const totalHeight = store.state.app.totalHeight
+
+    // 表格页码变化、排序、筛选回调
+    const handleTableChange = (pagination, filters, sorter) => {
+      emit("tableChange", pagination, filters, sorter);
+    }
+    // 单选回调
+    const handleSelect = (d, dl) => {
+      selectedRowKeys.value = d
+      if(props.selected) emit("selectChange", d, dl);
+    }
+    // 全选回调,需要手动填充数据
+    const handleSelectAll = (d) => {
+      let keys = [], dates = []
+      if(d) {
+        keys = props.dataSource.map(item => item[props.rowKey])
+        dates = [...props.dataSource]
+      }
+      handleSelect(keys, dates)
+    }
+
+    return {
+      totalHeight,
+      selectedRowKeys,
+      handleTableChange,
+      handleSelect,
+      handleSelectAll
+    }
+  }
+})
+</script>
+
+<style lang="less">
+  .c-table {
+    position: relative;
+    color: #000C17;
+    .totalTip {
+      position: relative;
+      top: -27px;
+    }
+    .ant-table-pagination.ant-pagination {
+      margin: 12px 0 0 0 ;
+    }
+    .ant-table-thead > tr > th {
+      background-color: #EEEFF3;
+      padding: 6px 3px;
+    }
+    .ant-table-tbody > tr > td {
+      padding: 3px 3px;
+    }
+    .light-row {
+      background-color: #FFFFFF;
+    }
+    .dark-row {
+      background-color: #F4F9FC;
+    }
+  }
+</style>

+ 262 - 0
src/components/editModel/index.vue

@@ -0,0 +1,262 @@
+<template>
+  <a-modal
+    :visible="visible"
+    :title="title"
+    :width="width"
+    :destroyOnClose="isDestroyOnClose"
+    :maskClosable="isMaskClosable"
+    @cancel="handleCancel"
+  >
+    <a-form
+      ref="editForm"
+      :model="formData"
+      :rules="rules"
+      :label-col="labelCol"
+      :wrapper-col="wrapperCol"
+    >
+      <a-row :gutter="24">
+        <a-col
+          v-for="(item, key) in formItems"
+          :key="key"
+          :span="item.span || 12"
+          v-show="!item.hide"
+        >
+          <a-form-item
+            :label="item.label"
+            :name="item.key"
+          >
+            <a-input
+              v-if="item.type === 'input'"
+              style="width: 100%"
+              :disabled="!!item.disabled"
+              v-model:value="formData[item.key]"
+              :placeholder="item.placeholder || '请输入'"
+              @change="(e) => item.needBack && handleBack(e.target.value, item.key)"
+            />
+
+            <a-input-number
+              v-if="item.type === 'number'"
+              style="width: 100%"
+              :min="item.min || 0"
+              :precision="item.precision || 0"
+              :disabled="!!item.disabled"
+              v-model:value="formData[item.key]"
+              :placeholder="item.placeholder || '请输入'"
+              @change="(e) => item.needBack && handleBack(e.target.value, item.key)"
+            />
+
+            <a-select
+              v-if="item.type === 'select'"
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              :disabled="!!item.disabled"
+              :mode="item.mode || null"
+              :show-search="!item.showSearch"
+              :allowClear="!!item.allowClear"
+              :placeholder="item.placeholder || '请选择'"
+              :getPopupContainer="(triggerNode) => triggerNode.parentNode"
+              :filter-option="(input, option) => {
+                return (
+                  option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                );
+              }"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            >
+              <a-select-option
+                v-for="(optionItem, optionKey) in item.option"
+                :key="optionKey"
+                :title="optionItem.title || optionItem.label"
+                :value="optionItem.value"
+              >
+                {{ optionItem.label }}
+              </a-select-option>
+            </a-select>
+
+            <large-select
+              v-if="item.type === 'largeSelect'"
+              v-model:value="formData[item.key]"
+              :disabled="!!item.disabled"
+              show-search
+              allowClear
+              :list="item.option"
+              @change="(v) => handleCustomChange(v, item)"
+            />
+
+            <batch-Input
+              v-if="item.type === 'batchInput'"
+              :disabled="!!item.disabled"
+              v-model:value="formData[item.key]"
+              :placeholder="item.placeholder || '请选择'"
+              @change="(e) => handleCustomChange(e.target.value, item)"
+            />
+
+            <a-date-picker
+              v-if="item.type === 'datepicker'"
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              :allowClear="item.allowClear || false"
+              :format="item.format || 'YYYY-MM-DD'"
+              :show-time="item.showTime"
+              :getCalendarContainer="(triggerNode) => triggerNode.parentNode"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            />
+
+            <a-range-picker
+              v-if="item.type === 'rangepicker'"
+              style="width: 110%"
+              v-model:value="formData[item.key]"
+              :format="item.format || 'YYYY-MM-DD'"
+              :disabled="!!item.disabled"
+              :show-time="!!item.showTime"
+              :allowClear="item.allowClear || false"
+              :placeholder="item.placeholder || ['开始日期', '结束日期']"
+              :getCalendarContainer="(triggerNode) => triggerNode.parentNode"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            >
+            </a-range-picker>
+
+            <a-radio-group
+              v-if="item.type === 'radio'"
+              v-model:value="formData[item.key]"
+              :disabled="!!item.disabled"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            >
+              <a-radio
+                v-for="(optionItem, optionKey) in item.option"
+                :key="optionKey"
+                :value="optionItem.value"
+              >
+                {{ optionItem.label }}
+              </a-radio>
+            </a-radio-group>
+          </a-form-item>
+        </a-col>
+      </a-row>
+    </a-form>
+
+    <template #footer>
+      <a-button key="back" :disabled="loading" @click="handleCancel">
+        取消
+      </a-button>
+      <a-button key="submit" type="primary" :loading="loading" @click="handleOk">
+        确定
+      </a-button>
+    </template>
+  </a-modal>
+</template>
+
+<script>
+import { defineComponent, ref, reactive, watch } from 'vue';
+import LargeSelect from '@components/largeSelect'
+import batchInput from '@components/batchInput/index'
+
+export default defineComponent({
+  name: 'editModel',
+  components: {
+    batchInput,
+    LargeSelect
+  },
+  props: {
+    loading: {
+      type: Boolean,
+      default: false
+    },
+    visible: {
+      // 弹窗是否显示
+      type: Boolean,
+      default: false
+    },
+    width: {
+      // 弹窗宽度
+      type: Number,
+      default: 800
+    },
+    title: {
+      // 弹窗标题
+      type: String,
+      default: ''
+    },
+    type: {
+      // 操作类型
+      type: String,
+      default: ''
+    },
+    isDestroyOnClose: {
+      // 是否销毁窗口
+      type: Boolean,
+      default: true
+    },
+    isMaskClosable: {
+      // 点击遮罩层是否关闭窗口
+      type: Boolean,
+      default: false
+    },
+    initData: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    formItems: {
+      type: Array,
+      default: () => {
+        return []
+      }
+    },
+  },
+  setup(props, { emit }) {
+    const editForm = ref();
+    let formData = ref({});
+    let rules = reactive({});
+
+    watch(() => props.visible, v => {
+      if(v) {
+        // 初始化表单默认值 和 校验规则
+        formData.value = props.type === 'edit' ? props.initData : {}
+        props.formItems.forEach(item => {
+          if(item.rules) {
+            rules[item.key] = [...item.rules]
+          } else {
+            rules[item.key] = {required: !!item.required, message: `请填写${item.label}`}
+          }
+        })
+      }
+    })
+
+    // antd3自定义表单组件绑定域还不支持,这里自定义组件暂时手动修改数据
+    const handleCustomChange = (v, d) => {
+      formData.value[d.key] = v
+      if(d.needBack) handleBack(e, d.key)
+    }
+    const handleBack = (v, k) => {
+      emit('onBack', v, k, formData.value)
+    }
+    const handleCancel = () => {
+      emit('onClose', 'cancle')
+    }
+    const handleOk = () => {
+      editForm.value.validate().then((v) => {
+        const params = {
+          type: props.type,
+          data: { ...v, id: props.initData.id || null }
+        }
+        emit('onClose', params)
+      })
+    }
+
+    return {
+      editForm,
+      formData,
+      rules,
+      labelCol: { span: 7 },
+      wrapperCol: { span: 17 },
+      handleCustomChange,
+      handleBack,
+      handleCancel,
+      handleOk,
+    }
+  }
+})
+</script>
+
+<style scoped></style>

+ 189 - 0
src/components/largeSelect/index.vue

@@ -0,0 +1,189 @@
+<template>
+  <div :class="['c-large-select', uid]">
+    <a-select 
+      style="width: 100%"
+      showSearch
+      :allowClear="allowClear"
+      :value="value"
+      :open="open"
+      :getPopupContainer="triggerNode => triggerNode.parentNode"
+      :filter-option="(input, option) => {
+        return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+      }"
+      :placeholder="placeholder"
+      @search="handleSearch"
+      @change="handleChange"
+      @dropdownVisibleChange="handleDrop"
+    >
+      <a-select-option v-for="op in selectData.list" :key="op.value" :title="op.title || op.label" :value="op.value">
+        {{op.label}}
+      </a-select-option>
+    </a-select>
+    <div v-show="open && selectData.res.length > rows" class="sc" :style="{height: listHeight+'px'}">
+      <div class="scbc" :style="{height: totalHeight+'px'}"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent, reactive, ref, onMounted, onBeforeUnmount } from 'vue';
+const ROWS = 8,           // 局部渲染的数据条数
+      HEIGHT = 31.6,      // 每行的高度
+      LISTHEIGHT = 250;   // 可视高度
+
+export default defineComponent({
+  name: 'largeSelect',
+  props: {
+    value: {
+      default: null
+    },
+    allowClear: {
+      default: false
+    },
+    placeholder: {
+      type: String,
+      default: "请选择"
+    },
+    // 下拉列表的list
+    list: {
+      type: Array,
+      default: () => []
+    },
+    rows: {   // 可视区域高度
+      type: Number,
+      default: ROWS
+    },
+    rowHeight: { // 每行的高度
+      type: Number,
+      default: HEIGHT
+    },
+    listHeight: { // 可是区域高度
+      type: Number,
+      default: LISTHEIGHT
+    },
+  },
+  setup(props, { emit }) {
+    let uid = ref('');            // 唯一id,用于监听事件
+    let totalHeight = ref(250.0);   // 总高度
+    let selectData = reactive({
+      list: [],                   // 下拉列表数据
+      res: [],                    // 下拉列表所有数据,主要针对搜索的情况需要及时变化滚动条
+    });
+    let open = ref(false);        // 下拉列表是否展开了
+    let idx = 0,                  // 当前开始下标
+        scrollEle = '',           // 滚动条dom对象
+        mousedown = false;        // 自定义滚动条按下事件标志
+
+    // 初始化事件
+    const initScroll = () => {
+      if(!scrollEle && selectData.res.length > props.rows) {
+        let node = document.querySelector(`.${uid.value} .sc`);
+        if(node) {
+          scrollEle = node
+          node.addEventListener('scroll', handleScroll);
+          node.addEventListener('mousedown', handleMouse(true));
+          node.addEventListener('mouseup', handleMouse(false));
+        }
+      }
+      if(scrollEle && selectData.res.length > props.rows) {
+        scrollEle.scrollTop = 0
+      }
+    }
+    // 监听鼠标事件的方法
+    const handleMouse = (v) => {
+      mousedown = v
+    }
+    // 监听虚拟滚轮变化,计算展示的数据
+    const handleScroll = (e) => {
+      const { scrollTop, scrollHeight } = e.target
+      let lenMax = selectData.res.length, nIdx;
+      if(scrollTop === 0) { // 滑到顶,从头展示
+        selectData.list = selectData.res.slice(0, props.rows)
+        idx = 0
+      } else if(scrollTop - (scrollHeight - props.listHeight) > -props.rowHeight ) { // 滑到底了,但是js监听精度不准,需要做一行的误差容错
+        nIdx = lenMax - props.rows
+        selectData.list = selectData.res.slice(nIdx, nIdx + props.rows)
+        idx = nIdx
+      } else {  // 其他情况,照常处理数据
+        nIdx = Math.ceil(scrollTop * lenMax / scrollHeight)
+        if(nIdx !== idx && nIdx <= (lenMax - props.rows)) {
+          selectData.list = selectData.res.slice(nIdx, nIdx + props.rows)
+          idx = nIdx
+        }
+      }
+    }
+    const handleSearch = (d) => {
+      let newList = props.list.filter(item => item.label.toLowerCase().indexOf(d.toLowerCase()) >= 0);
+      selectData.list = newList.slice(0, props.rows);
+      selectData.res = newList;
+      totalHeight.value = newList.length * props.rowHeight;
+    }
+    const handleChange = (d, op) => {
+      if(props.list.length > props.rows) {
+        let tar = props.list.find(item => item.value === d)
+        selectData.list = [tar, ...props.list.filter(item => item.value !== d).slice(0, props.rows - 1)]
+        selectData.res = [...props.list]
+        totalHeight.value = selectData.res.length * props.rowHeight
+      }
+      let tar = props.list.find(item => item.value === d)
+      emit('change', d, op, tar)
+    }
+    const handleDrop = (d) => {
+      open.value = mousedown ? true : d
+    }
+
+    selectData.list = props.list.length > props.rows ? props.list.slice(0, props.rows) : props.list;
+    selectData.res = props.list;
+    totalHeight.value = props.list.length * props.rowHeight
+    uid.value = `s_${(new Date()).valueOf()}`;
+
+    onMounted(() => {
+      initScroll();
+    })
+    onBeforeUnmount(() => {
+      if(scrollEle) {
+        let sc = document.querySelector(`.${uid.value} .sc`);
+        if(sc) {
+          sc.removeEventListener('scroll', handleScroll, false);
+          sc.removeEventListener('mousedown', handleMouse, false);
+          sc.removeEventListener('mouseup', handleMouse, false);
+        }
+        scrollEle = "";
+      }
+      open.value = false;
+      selectData.list = [];
+      selectData.res = [];
+    })
+
+    return {
+      uid,
+      totalHeight,
+      selectData,
+      open,
+      handleSearch,
+      handleChange,
+      handleDrop,
+    }
+  }
+})
+</script>
+
+<style lang="less" scoped>
+  .c-large-select {
+    position: relative;
+    .sc {
+      position: absolute;
+      top: 40px;
+      right: 0;
+      width: 16px;
+      overflow-x: hidden;
+      overflow-y: scroll;
+      z-index: 9999;
+      .scbc {
+        width: 15px;
+        border-radius: 2px;
+        background-color: #F1F1F1;
+      }
+    }
+  }
+</style>

+ 258 - 0
src/components/largeTable/index.vue

@@ -0,0 +1,258 @@
+<template>
+  <div class="c-large-table" :style="{paddingRight: dataSource.length > rows ? '10px' : 0}">
+    <a-table
+      :loading="loading"
+      :columns="tableColumns"
+      :row-key="record => record[rowKey]"
+      :data-source="state.tableData"
+      :row-selection="selected ? { selectedRowKeys: state.selectedRowKeys, onChange: handleSelect, onSelectAll: handleSelectAll } : null"
+      @change="handleTableChange"
+      :pagination="pagination"
+      :scroll="scroll"
+    >
+    </a-table>
+    <div v-show="dataSource.length > rows" class="sc" :style="{height: tableHeight+'px'}">
+      <div class="scbc" :style="{height: state.totalHeight+'px'}"></div>
+    </div>
+    <span class="totalTip">共 {{total}} 条数据</span>
+  </div>
+</template>
+
+<script>
+import { defineComponent, reactive, computed, watch, onMounted, onBeforeUnmount } from 'vue';
+const ROWS = 15,          // 局部渲染的数据条数
+      HEIGHT = 28.8,      // 每行的高度
+      TABLEHEIGHT = 433;  // 表格可视高度
+
+export default defineComponent({
+  name: 'largeTable',
+  props: {
+    loading: {
+      type: Boolean,
+      default: false
+    },
+    dataSource: {
+      type: Array,
+      default: []
+    },
+    rowKey: {
+      type: String,
+      default: "id"
+    },
+    columns: {
+      type: Array,
+      default: []
+    },
+    pagination: {
+      type: Object,
+      default: {
+        current: 1,
+        pageSize: 20,
+        tota: 0,
+        showSizeChanger: true,
+        showQuickJumper: true,
+        pageSizeOptions: ["20", "50", "100", "200", "500", "1000", "2000"]
+      }
+    },
+    rows: {   // 可视区域展示多少行
+      type: Number,
+      default: ROWS
+    },
+    rowHeight: { // 每行的高度
+      type: Number,
+      default: HEIGHT
+    },
+    tableHeight: { // 可是区域高度
+      type: Number,
+      default: TABLEHEIGHT
+    },
+    selected: { // 是否可选
+      type: Boolean,
+      default: false
+    },
+    scroll: { // 横向滚动,禁止纵向滚动
+      type: Object,
+      default: () => { return {x: true} }
+    },
+    selectChange: { // 可选的回调
+      type: Function,
+    }, 
+    tableChange: { // 分页、筛选、排序的回调
+      type: Function,
+    },
+    selectedRow: { // 分页、筛选、排序的回调
+      type: Array,
+      default: () => { return [] }
+    },
+    total: { 
+      type: Number,
+      default: 0
+    },
+  },
+  setup(props, { emit }){
+    const state = reactive({
+      scrollEle: '',
+      tableData: [],
+      selectedRowKeys: [],
+      totalHeight: 433,  // 数据总高度
+      idx: 0,            // 当前开始下标
+    });
+    const tableColumns = computed(() => {
+      let col = props.columns.map(item => {
+        if(!item.align) item.align = 'center'
+        if(item.maxLength) {
+          item.customRender = ({text}) => {
+            if(text && (text.length >= item.maxLength)) {
+              let short = text.slice(0, item.maxLength - 1)
+              return (<div class="slice-str">{short}...<p class="slice-str-all">{text}</p></div>)
+            } else {
+              return text
+            }
+          }
+        }
+        return item
+      })
+      return col
+    });
+    watch(() => props.dataSource, v => {
+      console.log('00000000000')
+      state.tableData = v.length > props.rows ? v.slice(0, props.rows) : v;
+      state.totalHeight = v.length * props.rowHeight;
+      initScroll();
+    });
+    watch(() => props.selectedRow, v => {
+      state.selectedRowKeys = v;
+    });
+    state.tableData = props.dataSource.length > props.rows ? props.dataSource.slice(0, props.rows) : props.dataSource;
+    state.totalHeight = props.dataSource.length * props.rowHeight;
+
+
+    const initScroll = () => {
+      if(!state.scrollEle && props.dataSource.length > props.rows) {
+        let node = document.querySelector('.c-large-table .sc');
+        if(node) {
+          state.scrollEle = node
+          document.querySelector('.c-large-table .sc').addEventListener('scroll', handleScroll);
+        }
+      }
+      if(state.scrollEle && props.dataSource.length > props.rows) {
+        state.scrollEle.scrollTop = 0
+      }
+    }
+    const onShowSizeChange =(current, pageSize) => {
+      emit("onShowSizeChange", current, pageSize);
+    }
+    const pageChange =(current, pageSize) => {
+      emit("onChange", current, pageSize);
+    }
+    const handleTableChange =(pagination, filters, sorter) => {
+      if(state.scrollEle) state.scrollEle.scrollTop = 0
+      emit("tableChange", pagination, filters, sorter);
+    }
+    const handleSelect =(d, dl) => {
+      state.selectedRowKeys = d
+      if(props.selected) emit("selectChange", d, dl);
+    }
+    // 注意全选,需要手动填充数据
+    const handleSelectAll =(d,dl) => {
+      let keys = [], dates = []
+      if(d) {
+        keys = props.dataSource.map(item => item[props.rowKey])
+        dates = [...props.dataSource]
+      }
+      handleSelect(keys, dates)
+    }
+    // 监听虚拟滚轮变化,计算展示的数据
+    const handleScroll =(e) => {
+      const { scrollTop, scrollHeight } = e.target
+      let lenMax = props.dataSource.length, nIdx;
+
+      if(scrollTop === 0) {
+        state.tableData = props.dataSource.slice(0, props.rows)
+        state.idx = 0
+      } else if(scrollTop === (scrollHeight - state.tableHeight)) {
+        nIdx = lenMax - state.rows
+        state.tableData = props.dataSource.slice(nIdx, nIdx + props.rows)
+        state.idx = nIdx
+      } else {
+        nIdx = Math.ceil(scrollTop * lenMax / scrollHeight)
+        if(nIdx !== state.idx && nIdx <= (lenMax - props.rows)) {
+          state.tableData = props.dataSource.slice(nIdx, nIdx + props.rows)
+          state.idx = nIdx
+        }
+      }
+    }
+
+    onMounted(() => {
+      initScroll();
+    });
+    onBeforeUnmount(() => {
+      if(state.scrollEle) {
+        document.querySelector('.c-large-table .sc').removeEventListener('scroll', handleScroll);
+        state.scrollEle = "";
+      }
+    });
+
+    return {
+      state,
+      tableColumns,
+      onShowSizeChange,
+      pageChange,
+      handleTableChange,
+      handleSelect,
+      handleSelectAll,
+      handleScroll,
+    }
+  }
+})
+</script>
+
+<style lang="less">
+  .c-large-table {
+    position: relative;
+    .totalTip {
+      position: relative;
+      top: -27px;
+    }
+    .ant-table-pagination.ant-pagination {
+      margin: 12px 0 0 0 ;
+    }
+    .slice-str {
+      position: relative;
+      cursor: default;
+      .slice-str-all {
+        position: absolute;
+        top: 18px;
+        left: 40px;
+        z-index: 99;
+        display: none;
+        min-width: 200px;
+        border-radius: 5px;
+        padding: 2px;
+        font-size: 12px;
+        background-color: rgba(0,0,0,0.8);
+        color: white;
+        text-align: left;
+      }
+      &:hover .slice-str-all{
+        display: block;
+      }
+    }
+    .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
+      padding: 3px 10px;
+    }
+    .sc {
+      position: absolute;
+      top: 25px;
+      right: -6px;
+      width: 16px;
+      overflow-x: hidden;
+      overflow-y: scroll;
+      .scbc {
+        width: 10px;
+        border-radius: 2px;
+        background-color: #F1F1F1;
+      }
+    }
+  }
+</style>

+ 280 - 0
src/components/searchModel/index.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="table-search-modal">
+    <a-form
+      ref="searchForm"
+      :model="formData"
+      :rules="rules"
+      :label-col="labelCol"
+      :wrapper-col="wrapperCol"
+    >
+      <a-row :gutter="24">
+        <a-col v-for="(item, idx) in formList" :key="idx" :span="6" :style="{ display: idx < count ? 'block' : 'none' }">
+          <a-form-item
+            v-if="item.type === 'input'"
+            :label="item.label"
+            :name="item.key"
+          >
+            <a-input
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              :placeholder="item.placeholder || '请输入'"
+              @change="(e) => item.needBack && handleBack(e.target.value, item.key)"
+            />
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'number'"
+            :label="`${item.label}`"
+            :name="item.key"
+          >
+            <a-input-number
+              style="width: 100%"
+              :placeholder="item.placeholder || '请输入'"
+              v-model:value="formData[item.key]"
+              :min="item.min || 0"
+              :precision="item.precision || 0"
+              @change="(e) => item.needBack && handleBack(e.target.value, item.key)"
+            />
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'select'"
+            :label="`${item.label}`"
+            :name="item.key"
+          >
+            <a-select
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              show-search
+              allowClear
+              :mode="item.mode || null"
+              :placeholder="item.placeholder || '请选择'"
+              :getPopupContainer="triggerNode => triggerNode.parentNode"
+              :filter-option="(input, option) => {
+                return (
+                  option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                );
+              }"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            >
+              <a-select-option
+                v-for="(optionItem,optionKey) in item.option"
+                :key="optionKey"
+                :title="optionItem.title || optionItem.label"
+                :value="optionItem.value"
+              >{{ optionItem.label }}</a-select-option>
+            </a-select>
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'largeSelect'"
+            :label="`${item.label}`"
+            :name="item.key"
+          >
+            <large-select
+              v-model:value="formData[item.key]"
+              show-search
+              allowClear
+              :list="item.option"
+              @change="(v) => handleCustomChange(v, item)"
+            />
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'batchInput'"
+            :label="`${item.label}`"
+            :name="item.key"
+          >
+            <batch-Input
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              :placeholder="item.placeholder || '请选择'"
+              @change="(e) => handleCustomChange(e.target.value, item)"
+            />
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'datepicker'"
+            :label="`${item.label}`"
+            :name="item.key"
+          >
+            <a-date-picker
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              :allowClear="item.allowClear || false"
+              :getCalendarContainer="triggerNode => triggerNode.parentNode"
+              :format="item.format || 'YYYY-MM-DD'"
+              :show-time="item.showTime"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            />
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'rangepicker'"
+            :label="`${item.label}`"
+            :name="item.key"
+          >
+            <a-range-picker
+              style="width: 100%"
+              v-model:value="formData[item.key]"
+              :allowClear="item.allowClear || false"
+              :getCalendarContainer="triggerNode => triggerNode.parentNode"
+              :format="item.format || 'YYYY-MM-DD'"
+              :show-time="!!item.showTime"
+              @change="(e) => item.needBack && handleBack(e, item.key)"
+            />
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'checkbox'"
+            :name="item.key"
+          >
+            <a-checkbox
+              :style="'width: 100%;'"
+              v-model:checked="formData[item.key]"
+              @change="(e) => item.needBack && handleBack(e.target.value, item.key)"
+            >{{item.label}}</a-checkbox>
+          </a-form-item>
+
+          <a-form-item
+            v-if="item.type === 'btn'"
+            style="width: 100%"
+            label=""
+            :labelCol="{span: 0}"
+          >
+            <div style="min-width: 230px;">
+              <a-button class="o-btn" @click="handleSearch">
+                <template #icon>
+                  <SearchOutlined />
+                </template>
+                查询
+              </a-button>
+              <a-button class="w-btn" style="margin-left: 10px" @click="handleReset">
+                <template #icon>
+                  <ReloadOutlined />
+                </template>
+                重置
+              </a-button>
+              <a style="margin-left: 8px; fontSize: 12px" @click="toggle" v-if="formList.length > 4">
+                {{ expand ? '合并' : '展开' }}
+                <UpOutlined v-if="expand" />
+                <DownOutlined v-else />
+              </a>
+            </div>
+          </a-form-item>
+        </a-col>
+      </a-row>
+    </a-form>
+
+    <slot class="left" name="tool-left"></slot>
+  </div>
+</template>
+
+<script>
+import { defineComponent, reactive, ref, computed } from 'vue';
+import LargeSelect from '@components/largeSelect'
+import batchInput from '@components/batchInput/index'
+import { UpOutlined, DownOutlined, SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue';
+
+export default defineComponent({
+  name: 'searchModel',
+  components: {
+    LargeSelect,
+    batchInput,
+    UpOutlined,
+    DownOutlined,
+    SearchOutlined,
+    ReloadOutlined
+  },
+  props: {
+    // needBack: true 需要回调 触发 onBack 函数;  某一选项变化的回调
+    formList: {
+      type: Array,
+      default: () => []
+    },
+    onSearch: {
+      type: Function
+    },
+    expanded: {
+      type: Boolean,
+      default: false
+    }
+  },
+  setup(props, { emit }) {
+    const searchForm = ref();
+    let formData = reactive({});
+    let rules = reactive({});
+    const expand = ref(props.expanded);
+    const count = computed(() => expand.value ? props.formList.length : 4);
+
+    // 初始化表单默认值 和 校验规则
+    props.formList.forEach(item => {
+      if(item.rules) {
+        rules[item.key] = [...item.rules]
+      } else {
+        rules[item.key] = {required: !!item.required, message: `请填写${item.label}`}
+      }
+      if(item.defaultValue || item.defaultValue === 0 || item.defaultValue === '') formData[item.key] = item.defaultValue
+    })
+
+    // 搜索的回调
+    const handleSearch = () => {
+      searchForm.value.validate().then((v) => {
+        emit('onSearch', v)
+      })
+    }
+    // 重置的回调
+    const handleReset = () => {
+      props.formList.forEach(item => {
+        formData[item.key] = (item.defaultValue || item.defaultValue === 0 ? item.defaultValue : null)
+      })
+      searchForm.value.validate().then((v) => {
+        emit('onSearch', v)
+      })
+    }
+    // 展开/隐藏高级搜索
+    const toggle = () => {
+      expand.value = !expand.value
+    }
+    // 筛选条件值变化的回调
+    const handleBack = (v, k) => {
+      emit('onBack', v, k)
+    }
+    // antd3自定义表单组件绑定域还不支持,这里自定义组件暂时手动修改数据
+    const handleCustomChange = (v, d) => {
+      formData[d.key] = v
+      if(d.needBack) handleBack(e, d.key)
+    }
+
+    return {
+      searchForm,
+      expand,
+      count,
+      formData,
+      rules,
+      labelCol: { span: 7 },
+      wrapperCol: { span: 17 },
+      toggle,
+      handleSearch,
+      handleReset,
+      handleBack,
+      handleCustomChange
+    }
+  }
+})
+</script>
+
+<style lang="less">
+  .table-search-modal {
+    width: 100%;
+    margin-bottom: 15px;
+    .left {
+      position: relative;
+      text-align: left;
+      width: auto;
+    }
+    .right {
+      text-align: right;
+    }
+  }
+</style>

+ 152 - 0
src/layouts/basicLayout.vue

@@ -0,0 +1,152 @@
+<template>
+  <div class="basic-layout">
+    <c-menu
+      :list="menu"
+    />
+    <div class="content">
+      <div class="header">
+        <div class="avatar">
+          <a-dropdown placement="bottomLeft">
+            <div class="user">
+              <a-avatar>
+                <template #icon>
+                  <UserOutlined />
+                </template>
+              </a-avatar>
+              <span class="name">{{user.name || ''}}</span>
+            </div>
+            <template #overlay>
+              <a-menu>
+                <a-menu-item @click="logout">退出登录</a-menu-item>
+              </a-menu>
+            </template>
+          </a-dropdown>
+        </div>
+      </div>
+      <div class="body">
+        <router-view />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent, ref, computed  } from 'vue';
+import { useRouter } from 'vue-router';
+import { useStore } from 'vuex';
+import { UserOutlined } from '@ant-design/icons-vue';
+import CMenu from '../components/c-menu/index.vue'
+import { ROUTE } from '@/router/menu'
+import { filterMenu } from '@/utils/util'
+
+export default defineComponent({
+  name: 'basicLayout',
+  components: {
+    CMenu,
+    UserOutlined
+  },
+  setup() {
+    const store = useStore();
+    const router = useRouter();
+    const menu = ref([]);         //菜单列表
+    const user = computed(() => store.state.app.user);              // 用户信息
+    const permission = computed(() => store.state.app.permission); // 权限
+
+    // 初始化时,获取用户信息、权限等
+    const getUserInfo = () => {
+      if(store.state.app.user && store.state.app.user.id) return
+      let p = [
+        { type: 'menu', path: '/report', key: '' },
+        { type: 'btn', path: '/report', key: 'add' },
+        { type: 'btn', path: '/report', key: 'edit' },
+        { type: 'btn', path: '/report', key: 'delete' },
+        { type: 'btn', path: '/report', key: 'export' },
+        { type: 'menu', path: '/setting/user/add' },
+        { type: 'menu', path: '/setting/user/edit' },
+        { type: 'menu', path: '/setting/user/delete' },
+        { type: 'menu', path: '/setting/role' },
+        { type: 'menu', path: '/login' },
+      ], a = {};
+      p.filter(item => item.type === 'btn').forEach(item => {
+        if(a[item.path]) {
+          a[item.path][item.key] = true
+        } else {
+          a[item.path] = {
+            [item.key]: true
+          }
+        }
+      })
+      store.commit('app/setUser', {
+        user: { id: 1186, name: '张三' },
+        permission: p,
+        auth: a
+      })
+    }
+    getUserInfo();
+
+    // 按权限过滤菜单,转一下json,避免对象原型修改
+    let m = JSON.parse(JSON.stringify(ROUTE[0].children)),
+    permi = permission.value.filter(item => item.type === 'menu'),
+    list = filterMenu(m, permi);
+    menu.value = list;
+
+    // 菜单权限控制,路由拦截。注意,组件内部拦截,刷新界面、输入地址时,钩子函数不会触发
+    const controlMenu = () => {
+      // 刷新界面、输入地址时,校验权限
+      let flag =  permission.value.some(item => item.path === window.location.pathname);
+      if(!flag) {
+        router.push('/login')
+      }
+      router.beforeEach((to, from, next) => {
+        let flag =  permission.value.some(item => item.path === to.path);
+        if(flag) {
+          next();
+        } else {
+          router.push('/login')
+        }
+      })
+    };
+    // 退出登录
+    const logout = () => {
+      router.push('/login')
+    };
+    controlMenu();
+
+    return {
+      menu,
+      user,
+      logout
+    }
+  }
+})
+</script>
+
+<style lang="less" scoped>
+.basic-layout {
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+  display: flex;
+  justify-content: space-between;
+  .content {
+    width: 100%;
+    .header {
+      height: 50px;
+      position: relative;
+      box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
+      .avatar {
+        position: absolute;
+        right: 10px;
+        line-height: 50px;
+        cursor: pointer;
+        .name {
+          padding: 0 10px;
+        }
+      }
+    }
+    .body {
+      padding: 10px;
+    }
+  }
+}
+</style>

+ 19 - 0
src/layouts/pageLayout.vue

@@ -0,0 +1,19 @@
+<template>
+  <div class="page-layout">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+  data () {
+    return {}
+  },
+  computed: {},
+  created () {},
+  methods: {}
+}
+</script>
+
+<style lang="less" scoped>
+</style>

+ 24 - 0
src/main.js

@@ -0,0 +1,24 @@
+import { createApp } from 'vue'
+import dayjs from 'dayjs'
+import App from './App.vue'
+import './antdUse'
+import router from './router'
+import store from './store'
+import antdUse from './antdUse'
+import 'dayjs/locale/zh-cn';
+import './app.less'
+
+// 创建应用上下文
+const app = createApp(App)
+app.config.productionTip
+dayjs.locale('zh-cn');
+// 原型上添加全局字段方式变了
+app.config.globalProperties.$dayjs = dayjs
+// 引入路由、vuex、 antd
+antdUse(app)
+app.use(router)
+app.use(store)
+
+// 挂载dom
+app.mount('#app')
+

+ 10 - 0
src/router/index.js

@@ -0,0 +1,10 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import { ROUTE } from './menu.js'
+
+const route = createRouter({
+    history: createWebHistory(process.env.BASE_URL),
+    scrollBehavior: () => ({ y: 0 }),
+    routes: ROUTE
+})
+
+export default route

+ 29 - 0
src/router/menu.js

@@ -0,0 +1,29 @@
+import BasicLayout from '../layouts/basicLayout.vue'
+import PageLayout from '../layouts/pageLayout.vue'
+export const ROUTE = [
+  {
+    path: '/',
+    name: 'main',
+    redirect: '/report',
+    children: [
+      {
+        path: '/report',
+        name: 'report',
+        title: '报表中心',
+        component: () => import('@views/report.vue')
+      },
+    ]
+  },
+  {
+    path: '/login',
+    name: 'login',
+    title: '登陆',
+    hidden: true,
+    component: () => import('@views/login.vue')
+  },
+  {
+    path: '/:catchAll(.*)',
+    redirect: '/404',
+    hidden: true
+  }
+]

+ 13 - 0
src/store/index.js

@@ -0,0 +1,13 @@
+import { createStore } from 'vuex'
+import app from './modules/app.js'
+
+const store = createStore({
+  modules: {
+    app
+  },
+  state: () => ({}),
+  mutations: {},
+  actions: {},
+  getters: {}
+})
+export default store

+ 28 - 0
src/store/modules/app.js

@@ -0,0 +1,28 @@
+const app = {
+    namespaced: 'app',
+    state: () => ({
+      token: '3mnuehn457873nberuwen',
+      user: {},
+      factoryList: [
+        {value: '001', label: '华为'},
+        {value: '002', label: '谷歌'},
+        {value: '003', label: '中芯国际'},
+        {value: '004', label: '宁德时代'}
+      ],
+      // 所有权限. type 类型 区分菜单和按钮  path权限所在的菜单路径  key 是权限关键字,按钮才有
+      permission: [],
+      auth: {},     //   按钮权限
+    }),
+    mutations: {
+      setToken: (state, d) => {
+        state.token = d
+      },
+      setUser: (state, d) => {
+        state.user = d.user
+        state.permission = d.permission
+        state.auth = d.auth
+      }
+    },
+    actions: {}
+  }
+  export default app

+ 79 - 0
src/style.css

@@ -0,0 +1,79 @@
+:root {
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
+
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
+
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+body {
+  margin: 0;
+  display: flex;
+  place-items: center;
+  min-width: 320px;
+  min-height: 100vh;
+}
+
+h1 {
+  font-size: 3.2em;
+  line-height: 1.1;
+}
+
+button {
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 0.6em 1.2em;
+  font-size: 1em;
+  font-weight: 500;
+  font-family: inherit;
+  background-color: #1a1a1a;
+  cursor: pointer;
+  transition: border-color 0.25s;
+}
+button:hover {
+  border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+  outline: 4px auto -webkit-focus-ring-color;
+}
+
+.card {
+  padding: 2em;
+}
+
+#app {
+  max-width: 1280px;
+  margin: 0 auto;
+  padding: 2rem;
+  text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+  a:hover {
+    color: #747bff;
+  }
+  button {
+    background-color: #f9f9f9;
+  }
+}

+ 31 - 0
src/utils/exportExcel.js

@@ -0,0 +1,31 @@
+/* import XLSX from 'xlsx'; */
+
+/* 
+  备注:直接传入表格的 columns  和 data即可
+
+  header: [{dataIndex: 'site', title: '地点'}]      string Array    标题
+  body: [{time: '3点', site: '青岛'}]       object Array    数据
+  name: '数据导出'                          string          文件名
+*/
+/* export function expoerExcel(header = [], body = [], name = '') {
+  // 处理标题
+  const dataSource = [], r1 = [];
+  header.forEach(item => {
+    r1.push(item.title)
+  })
+  dataSource.push(r1)
+
+  // 处理数据
+  body.forEach(b => {
+    let r = [];
+    header.forEach(h => {
+      r.push(b[h.dataIndex])
+    })
+    dataSource.push(r)
+  })
+
+  const worksheet = XLSX.utils.aoa_to_sheet(dataSource);
+  const new_workbook = XLSX.utils.book_new();  
+  XLSX.utils.book_append_sheet(new_workbook, worksheet, 'sheet1');
+  XLSX.writeFile(new_workbook, `${name || '导出报表'}.xlsx`);
+} */

+ 64 - 0
src/utils/request.js

@@ -0,0 +1,64 @@
+import axios from 'axios'
+import router from '@router/index.js'
+import store from '@store/index.js'
+import notification from 'ant-design-vue/es/notification'
+
+// 创建 axios 实例
+const service = axios.create({
+    baseURL: 'https://qlaiapi.raycos.com.cn', // api base_url
+    withCredentials: true,
+    timeout: 60000, // 超时时间:6分钟 注意和nginx配置保持一致
+    maxBodyLength: 31457280,  // 请求体最大长度 单位B,上限30MB 注意和nginx配置保持一致
+    maxContentLength: 5242880,  // 响应的最大长度,单位 B ,5MB,针对文件或者表格数据大量返回 注意和nginx配置保持一致
+})
+
+const err = (error) => {
+    if (error.response) {
+        const data = error.response.data
+        if (error.response.status === 401) { // 登录失效
+            router.push({path: '/login'})
+        } else if (error.response.status === 402) {  // 缺少权限
+            notification.error({
+                message: '您没有权限查看当前信息',
+                description: '请联系管理员获取数据权限',
+                duration: null
+            })
+        } else if (error.response.status === 403) {
+            notification.error({
+                message: 'Forbidden',
+                description: data.message
+            })
+        } else if (error.response.status === 504) {  // 请求超时
+            notification.error({
+                message: '提示',
+                description: '请求时间较长,请稍后查看',
+                duration: null
+            });
+        } 
+    } else {
+        // 请求超时
+        notification.error({
+            message: '提示',
+            description: '请求时间较长,请稍后查看',
+            duration: null
+        });
+    }
+    return Promise.reject(error)
+}
+
+// 请求拦截,设置token
+service.interceptors.request.use((config) => {
+    config.headers['Access-Token'] = store.state.app.token || ''
+    return config
+}, err)
+
+// 处理响应
+service.interceptors.response.use((response) => {
+    return response.data
+}, err)
+
+function requests(options = {}) {
+    return service.request({ ...options })
+}
+
+export { requests as axios }

+ 56 - 0
src/utils/util.js

@@ -0,0 +1,56 @@
+// 遍历路由,获取左侧主菜单, 精简字段,并按照权限过滤
+export const filterMenu = (l, p) => {
+    let arr = [];
+    for(let i = 0; i < l.length; i++) {
+      // 校验权限,权限存储底层路由即可,父级菜单自动通过
+      let able = p.some(item => item.path.indexOf(l[i].path) === 0)
+      if (!l[i].hidden && able) {
+        let obj = {...l[i]};
+        delete obj.component
+        if (l[i].children && l[i].children.length) {
+          obj.children = filterMenu(l[i].children, p)
+        }
+        arr.push(obj)
+      }
+    }
+    return arr
+  }
+  
+  // url传参,处理参数
+  export function makeQuery (obj = {}) {
+    let arr = [], str = '';
+    for (let key in obj) {
+        if (obj[key] || obj[key] === 0 || obj[key] === false) {
+            arr.push(`${key}=${obj[key]}`)
+        }
+    }
+    str = arr.join('&')
+    return str ? `?${str}` : ''
+  }
+  
+  // 表单自定义校验,校验长度
+  export function checklength(_, value) {
+    if (value) {
+      const arr = value.split(',')
+      if (arr.length > 500) {
+        return Promise.reject(new Error('最大支持500条数据'));
+      } else {
+        return Promise.resolve();
+      }
+    } else {
+      return Promise.resolve();
+    }
+  }
+  // 表单自定义校验,校验重复性
+  export function checkRepeat (_, value) {
+    if (value) {
+      const arr = value.split(',')
+      if ((new Set(arr)).size != arr.length) {
+        return Promise.reject(new Error('存在重复信息'));
+      } else {
+        return Promise.resolve();
+      }
+    } else {
+      return Promise.resolve();
+    }
+  }

+ 124 - 0
src/views/login.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="login-page">
+    <div class="login">
+      <a-form 
+        ref="loginForm"
+        :model="formData"
+        :rules="rules"
+        :label-col="labelCol"
+        :wrapper-col="wrapperCol"
+      >
+        <a-form-item label="账号" name="username">
+          <a-input
+            style="width: 206px"
+            v-model:value="formData.username"
+          />
+        </a-form-item>
+        <a-form-item label="密码" name="password">
+          <a-input
+            style="width: 206px"
+            v-model:value="formData.password"
+          />
+        </a-form-item>
+        <div class="item">
+          <a-button class="btn" type="primary" @click="login">登陆</a-button>
+        </div>
+      </a-form>
+    </div>
+  </div>
+</template>
+
+<script>
+import { defineComponent, reactive, ref } from 'vue';
+import { useRouter } from 'vue-router';
+import { useStore } from 'vuex';
+
+export default defineComponent({
+  name: 'login',
+  setup() {
+    const store = useStore();
+    const router = useRouter();
+    const loginForm = ref();
+    const formData = reactive({
+      username: '',
+      password: ''
+    });
+    const rules = {
+      username: [{required: true, message: '请填写账号'}],
+      password: [{required: true, message: '请填写密码'}]
+    };
+
+    // 登陆
+    const login = () => {
+      loginForm.value.validate().then((v) => {
+        let permission = [
+            { type: 'menu', path: '/report', key: '' },
+            { type: 'btn', path: '/report', key: 'add' },
+            { type: 'btn', path: '/report', key: 'edit' },
+            { type: 'btn', path: '/report', key: 'delete' },
+            { type: 'btn', path: '/report', key: 'export' },
+            { type: 'menu', path: '/setting/user/add' },
+            { type: 'menu', path: '/setting/user/edit' },
+            { type: 'menu', path: '/setting/user/delete' },
+            { type: 'menu', path: '/setting/role' },
+            { type: 'menu', path: '/login' },
+          ], auth = {};
+        permission.filter(item => item.type === 'btn').forEach(item => {
+          if(auth[item.path]) {
+            auth[item.path][item.key] = true
+          } else {
+            auth[item.path] = {
+              [item.key]: true
+            }
+          }
+        })
+        store.commit('app/setUser', {
+          user: { id: 1186, name: '张三' },
+          permission,
+          auth
+        })
+        router.push('/')
+      })
+      .catch(error => {
+        console.log('error', error);
+      });
+    }
+
+    return {
+      loginForm,
+      formData,
+      rules,
+      labelCol: { span: 5 },
+      wrapperCol: { span: 12 },
+      login
+    }
+  }
+})
+</script>
+
+<style lang="less" scoped>
+.login-page {
+  position: relative;
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+  .login {
+    position: absolute;
+    top: 20%;
+    right: 10%;
+    width: 300px;
+    padding: 30px 20px;
+    box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
+    border-radius: 8px;
+    .item {
+      width: 100%;
+      text-align: center;
+      margin-top: 15px;
+      .btn {
+        width: 60%;
+        margin: 0 auto;
+      }
+    }
+  }
+}
+</style>

+ 1711 - 0
src/views/report.vue

@@ -0,0 +1,1711 @@
+<template>
+  <div class="layout">
+    <div class="menu">
+      <!-- Logo -->
+      <div class="logo">
+        <img
+          src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*eco6RrQhxbMAAAAAAAAAAAAADgCCAQ/original"
+          :draggable="false"
+          alt="logo"
+          class="logo-img"
+        />
+        <span>Ant Design X</span>
+      </div>
+
+      <!-- New Conversation 按钮 -->
+      <button class="new-conversation-btn" @click="addNewConversation">
+        <!-- <PlusOutlined /> -->
+        <img class="tab-icon" src="../assets/svg/plus.svg" alt="" />
+        <span>New Conversation</span>
+      </button>
+
+      <!-- 会话列表 -->
+      <div class="conversation-list">
+        <div
+          v-for="conversation in conversations"
+          :key="conversation.id"
+          :class="[
+            'conversation-item',
+            { active: currentConversationId === conversation.id },
+          ]"
+          @click="switchConversation(conversation.id)"
+        >
+          {{ conversation.title }}
+        </div>
+      </div>
+    </div>
+
+    <div class="chat">
+      <!-- 欢���消息 -->
+      <div v-if="messages.length === 0" class="welcome-section">
+        <div class="welcome-header">
+          <img
+            class="avatar"
+            src=""
+            alt="Ant Design X"
+          /><!-- path-to-your-avatar , I'm Ant Design X-->
+          <div class="welcome-info">
+            <h2>Hello</h2>
+            <p>
+              Base on Ant Design, AGI product interface solution, create a
+              better intelligent vision~
+            </p>
+          </div>
+          <div class="action-buttons">
+            <button class="icon-btn"><!-- <ShareAltOutlined /> --></button>
+            <button class="icon-btn"><!-- <EllipsisOutlined /> --></button>
+          </div>
+        </div>
+
+        <!-- Do you want 部分 -->
+        <div class="suggestion-section">
+          <h3>Do you want?</h3>
+
+          <div class="cards-container">
+            <!-- Hot Topics 卡片 -->
+            <div class="suggestion-card">
+              <div class="card-header">
+                <img
+                  src="../assets/svg/hot-for-ux.svg"
+                  alt="hot topics"
+                  class="tab-icon"
+                />
+                <h4>Hot Topics</h4>
+              </div>
+              <p class="card-subtitle">What are you interested in?</p>
+              <div class="suggestion-items">
+                <div class="suggestion-item">What's new in X?</div>
+                <div class="suggestion-item">What's AGI?</div>
+                <div class="suggestion-item">Where is the doc?</div>
+              </div>
+            </div>
+
+            <!-- Design Guide 卡片 -->
+            <div class="suggestion-card">
+              <div class="card-header">
+                <img
+                  src="../assets/svg/read.svg"
+                  alt="hot topics"
+                  class="tab-icon"
+                />
+                <h4>Design Guide</h4>
+              </div>
+              <p class="card-subtitle">How to design a good product?</p>
+              <div class="suggestion-items">
+                <div class="suggestion-item">
+                  <!-- <HeartOutlined /> -->
+                  Know the well
+                </div>
+                <div class="suggestion-item">
+                  <!-- <SmileOutlined /> -->
+                  Set the AI role
+                </div>
+                <div class="suggestion-item">
+                  <!--  <CommentOutlined /> -->
+                  Express the feeling
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 修改消息列表部分 -->
+      <div v-else class="message-list" ref="messageList">
+        <div
+          v-for="message in messages"
+          :key="message.id"
+          :class="[
+            'message-item',
+            message.role === 'user' ? 'user' : 'assistant',
+          ]"
+        >
+          <div class="message-bubble">
+            <div class="message-content">
+              <template v-if="message.role === 'user' && message.files">
+                <div
+                  v-for="file in message.files"
+                  :key="file.name"
+                  class="file-message"
+                >
+                  <!-- 图片文件显示 -->
+                  <template v-if="isImageFile(file)">
+                    <div class="image-preview">
+                      <img :src="file.url" class="preview-image" />
+                      <!-- <div class="image-info">
+                        <span>解析图片</span>
+                      </div> -->
+                    </div>
+                  </template>
+
+                  <!-- 文档文件显示 -->
+                  <template v-else>
+                    <div class="file-info-display">
+                      <img :src="getFileIcon(file)" class="file-type-icon" />
+                      <div class="file-details">
+                        <span class="file-name">{{ file.name }}</span>
+                        <span class="file-size">{{ file.size }}</span>
+                      </div>
+                    </div>
+                    <!--  <div class="file-action">解析文档中的内容</div> -->
+                  </template>
+                </div>
+              </template>
+              <div>
+                {{
+                  message.role === "assistant"
+                    ? message.displayContent
+                    : message.content
+                }}
+              </div>
+
+              <!-- 音频控制按钮 -->
+              <div
+                v-if="message.role === 'assistant' && message.audioData"
+                class="audio-controls"
+              >
+                <button
+                  class="audio-btn"
+                  @click="
+                    isPlaying
+                      ? stopAudio()
+                      : playAudioMessage(message.audioData)
+                  "
+                >
+                  <div v-if="isPlaying" class="wave-animation">
+                    <span class="wave-bar"></span>
+                    <span class="wave-bar"></span>
+                    <span class="wave-bar"></span>
+                    <span class="wave-bar"></span>
+                  </div>
+                  <img v-else :src="playIcon" alt="播放" class="audio-icon" />
+                </button>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 输入框 -->
+      <div class="input-container">
+        <!-- 标签栏 -->
+        <div class="tabs-wrapper">
+          <div class="tab-item active">
+            <img
+              src="../assets/svg/hot-for-ux.svg"
+              alt="hot topics"
+              class="tab-icon"
+            />
+            <span>Hot Topics</span>
+          </div>
+          <div class="tab-item">
+            <img
+              src="../assets/svg/read.svg"
+              alt="hot topics"
+              class="tab-icon"
+            />
+            <span>Design Guide</span>
+          </div>
+        </div>
+        <!-- 添加附件面板 -->
+        <div v-if="showAttachments" class="attachments-panel">
+          <div class="panel-content">
+            <div class="attachments-header">
+              <span>Attachments</span>
+              <button class="close-btn" @click="toggleAttachments">
+                <img src="../assets/svg/close.svg" alt="" />
+              </button>
+            </div>
+            <div
+              v-if="attachedFiles.length === 0"
+              style="display: flex; justify-content: center"
+            >
+              <div
+                class="upload-area"
+                :class="{ 'has-files': attachedFiles.length > 0 }"
+                @click="triggerFileInput"
+                @drop.prevent="handleFileDrop"
+                @dragover.prevent
+                @dragenter.prevent
+              >
+                <img
+                  src="../assets/svg/upload.svg"
+                  alt="upload"
+                  class="upload-icon"
+                />
+                <div class="upload-text">Upload files</div>
+                <div class="upload-hint">
+                  Click or drag files to this area to upload
+                </div>
+                <input
+                  type="file"
+                  ref="fileInput"
+                  style="display: none"
+                  @change="handleFileUpload"
+                  multiple
+                />
+              </div>
+            </div>
+            <!-- 上传内容列表 -->
+            <div v-if="attachedFiles.length !== 0">
+              <div class="file-list">
+                <div
+                  v-for="file in attachedFiles"
+                  :key="file.name"
+                  class="file-item"
+                  :class="{ 'is-image': isImageFile(file) }"
+                >
+                  <div :class="isImageFile(file) ? 'file-wrapper' : ''">
+                    <img
+                      :src="getFileIcon(file)"
+                      alt="file"
+                      :class="isImageFile(file) ? 'file-icon' : ''"
+                    />
+                    <button class="remove-btn" @click.stop="removeFile(file)">
+                      ×
+                    </button>
+                  </div>
+                  <div class="file-info" v-if="!isImageFile(file)">
+                    <span class="file-name">{{ file.name }}</span>
+                    <span class="file-size">{{ file.sizeFormatted }}</span>
+                  </div>
+                </div>
+                <button class="add-more-btn" @click.stop="triggerFileInput">
+                  <img src="../assets/svg/plus.svg" alt="add" />
+                </button>
+              </div>
+
+              <input
+                type="file"
+                ref="fileInput"
+                style="display: none"
+                @change="handleFileUpload"
+                multiple
+              />
+            </div>
+            <!--   <div v-if="attachedFiles.length" class="attached-files">
+              <div
+                v-for="file in attachedFiles"
+                :key="file.name"
+                class="file-item"
+              >
+                <FileOutlined />
+                <span>{{ file.name }}</span>
+                <button class="remove-btn" @click="removeFile(file)">
+                </button>
+              </div>
+            </div> -->
+          </div>
+        </div>
+        <!-- 输入框部分 -->
+        <div class="input-wrapper">
+          <button class="attachment-btn" @click="toggleAttachments">
+            <img
+              src="../assets/svg/attachment.svg"
+              alt="attachment"
+              class="tab-icon"
+            />
+          </button>
+          <input
+            type="text"
+            class="message-input"
+            v-model="inputContent"
+            @keyup.enter="sendMessage"
+            placeholder="Type a message..."
+          />
+          <button
+            class="send-btn"
+            @click="sendMessage"
+            :disabled="!inputContent && attachedFiles.length === 0"
+          >
+            <img src="../assets/svg/up.svg" alt="send" />
+          </button>
+        </div>
+      </div>
+    </div>
+
+    <div class="right_menu" :class="{ collapsed: isRightMenuCollapsed }">
+      <div class="collapse-button" @click="toggleRightMenu">
+        <img
+          :src="isRightMenuCollapsed ? leftIcon : rightIcon"
+          alt="toggle"
+          class="collapse-icon"
+        />
+      </div>
+      <div class="right-menu-content">
+        <!-- 这里添加右侧菜单的内容 -->
+        <h3>Right Menu</h3>
+        <p>Add your content here</p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick } from "vue";
+import {
+  PlusOutlined,
+  FireOutlined,
+  ReadOutlined,
+  HeartOutlined,
+  SmileOutlined,
+  CommentOutlined,
+  CloudUploadOutlined,
+  PaperClipOutlined,
+  ShareAltOutlined,
+  EllipsisOutlined,
+  CloseOutlined,
+  FileOutlined,
+  ArrowUpOutlined,
+  UploadOutlined,
+} from "@ant-design/icons-vue";
+import leftIcon from "../assets/svg/left.svg";
+import rightIcon from "../assets/svg/right.svg";
+import plusIcon from "../assets/svg/plus.svg";
+import hotForUxIcon from "../assets/svg/hot-for-ux.svg";
+import readIcon from "../assets/svg/read.svg";
+import attachmentIcon from "../assets/svg/attachment.svg";
+import uploadIcon from "../assets/svg/upload.svg";
+import closeIcon from "../assets/svg/close.svg";
+import upIcon from "../assets/svg/up.svg";
+import wordIcon from "../assets/svg/word.svg";
+import pauseIcon from "../assets/svg/pause.svg";
+import playIcon from "../assets/svg/play.svg";
+import axios from "axios";
+import { useStore } from "vuex";
+
+// 响应式状态
+const headerOpen = ref(false);
+const inputContent = ref("");
+const showAttachments = ref(false);
+const isRightMenuCollapsed = ref(false);
+const fileInput = ref(null);
+const attachedFiles = ref([]);
+const messages = ref([]);
+const isLoading = ref(false);
+const currentConversationId = ref(1);
+const conversationCounter = ref(1);
+const messageList = ref(null);
+const store = useStore();
+
+// 会话数据
+const conversations = ref([
+  {
+    id: 1,
+    title: "What is Ant Design X?",
+    messages: [],
+  },
+]);
+
+// 添加打字效果相关的状态
+const typingSpeed = 50; // 打字速度(毫秒/字符)
+const isTyping = ref(false);
+
+// 添加音频播放相关的响应式变量
+const audioPlayer = ref(null);
+const isPlaying = ref(false);
+
+// 方法
+const toggleAttachments = () => {
+  showAttachments.value = !showAttachments.value;
+};
+
+const removeFile = (file) => {
+  attachedFiles.value = attachedFiles.value.filter((f) => f !== file);
+};
+
+const triggerFileInput = () => {
+  fileInput.value.click();
+};
+
+const uploadFile = async (file) => {
+  try {
+    const formData = new FormData();
+    formData.append("file", file);
+
+    const response = await axios.post(
+      "https://ylaiapi.raycos.com.cn/upload/file",
+      formData,
+      {
+        headers: {
+          "Content-Type": "multipart/form-data",
+        },
+      }
+    );
+
+    if (response.data.status === 200) {
+      return response.data.data.fileUrl; // 返回文件上传后的URL
+    } else {
+      throw new Error(response.data.message || "文件上传失败");
+    }
+  } catch (error) {
+    console.error("文件上传失败:", error);
+    throw error;
+  }
+};
+
+const handleFileUpload = async (event) => {
+  const files = Array.from(event.target.files);
+
+  try {
+    isLoading.value = true;
+
+    const uploadPromises = files.map(async (file) => {
+      const url = await uploadFile(file);
+      return {
+        name: file.name,
+        size: file.size, // 保存原始大小(字节)
+        sizeFormatted: formatFileSize(file.size), // 格式化后的大小
+        type: file.type,
+        url: url,
+      };
+    });
+
+    const uploadedFiles = await Promise.all(uploadPromises);
+    attachedFiles.value = [...attachedFiles.value, ...uploadedFiles];
+  } catch (error) {
+    console.error("文件上传失败:", error);
+  } finally {
+    isLoading.value = false;
+    event.target.value = "";
+  }
+};
+
+const formatFileSize = (bytes) => {
+  if (!bytes || isNaN(bytes)) return "0 B";
+  const k = 1024;
+  const sizes = ["B", "KB", "MB", "GB"];
+  const i = Math.floor(Math.log(bytes) / Math.log(k));
+  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
+};
+
+const getFileIcon = (file) => {
+  // 如果文件已经有 URL 且是图片类型,返回 URL
+  if (file.url && isImageFile(file)) {
+    return file.url;
+  }
+
+  // 其他所有情况返回文档图标
+  return wordIcon;
+};
+
+const isImageFile = (file) => {
+  const imageTypes = ["jpg", "jpeg", "png", "gif", "webp"];
+  const ext = file.name?.split(".")?.pop()?.toLowerCase() || "";
+  return imageTypes.includes(ext);
+};
+
+const getFileType = (file) => {
+  const ext = file.name.split(".").pop().toLowerCase();
+  const imageTypes = ["jpg", "jpeg", "png", "gif", "webp"];
+  return imageTypes.includes(ext);
+};
+
+// 添加滚动到底部的方法
+const scrollToBottom = () => {
+  if (messageList.value) {
+    nextTick(() => {
+      messageList.value.scrollTop = messageList.value.scrollHeight;
+    });
+  }
+};
+
+// 添加 session_id 的响应式引用
+const session_id = ref("");
+// 修改发送消息方法
+const sendMessage = async () => {
+  if (
+    (!inputContent.value.trim() && attachedFiles.value.length === 0) ||
+    isTyping.value
+  )
+    return;
+
+  // 构建消息内容,包含文件信息
+  let messageContent = inputContent.value.trim();
+
+  // 收集文件URLs
+  const imageUrls = [];
+  const documentUrls = [];
+
+  attachedFiles.value.forEach((file) => {
+    if (getFileType(file)) {
+      imageUrls.push(file.url);
+    } else {
+      documentUrls.push(file.url);
+    }
+  });
+
+  // 添加用户消息
+  const userMessage = {
+    id: Date.now(),
+    role: "user",
+    content: messageContent,
+    displayContent: messageContent,
+    files: attachedFiles.value.map((file) => ({
+      name: file.name,
+      size: file.sizeFormatted, // 使用格式化后的大小
+      type: file.type,
+      url: file.url,
+    })),
+  };
+  messages.value.push(userMessage);
+  scrollToBottom();
+
+  // 保存当前输入内容并清空输入框和附件
+  const currentInput = inputContent.value;
+  showAttachments.value = false;
+  inputContent.value = "";
+  const currentFiles = [...attachedFiles.value];
+  attachedFiles.value = [];
+
+  // 添加临时AI消息
+  const tempAiMessage = {
+    id: Date.now() + 1,
+    role: "assistant",
+    content: "Thinking...",
+    displayContent: "Thinking...",
+    isLoading: true,
+  };
+  messages.value.push(tempAiMessage);
+  scrollToBottom();
+
+  try {
+    const response = await axios.post(
+      "https://ylaiapi.raycos.com.cn/chatbot/multimodal",
+      {
+        message: currentInput,
+        chat_config_id: "17",
+        user_id: "13365429324",
+        session_id: session_id.value,
+        source: "pc",
+        image_urls: imageUrls,
+        documents: documentUrls,
+      }
+    );
+
+    // 更新 session_id
+    if (response.data.data.session_id) {
+      session_id.value = response.data.data.session_id;
+    }
+
+    // 移除时消息并添加AI响应
+    messages.value = messages.value.filter(
+      (msg) => msg.id !== tempAiMessage.id
+    );
+    const aiMessage = reactive({
+      id: Date.now() + 2,
+      role: "assistant",
+      content: response.data.data.answer,
+      displayContent: "",
+      audioData: response.data.data.audio_info?.audio || null, // 保存音频数据
+    });
+
+    messages.value.push(aiMessage);
+    // 如果有音频数据,自动播放
+    if (aiMessage.audioData) {
+      playAudioMessage(aiMessage.audioData);
+    }
+    await typeMessage(aiMessage);
+  } catch (error) {
+    console.error("发送消息失败:", error);
+    messages.value = messages.value.filter(
+      (msg) => msg.id !== tempAiMessage.id
+    );
+    messages.value.push({
+      id: Date.now() + 2,
+      role: "assistant",
+      content: "Sorry, there was an error processing your request.",
+      displayContent: "Sorry, there was an error processing your request.",
+    });
+  } finally {
+    isTyping.value = false;
+    await nextTick();
+    scrollToBottom();
+  }
+};
+
+// 修改打字效果方法
+const typeMessage = async (message) => {
+  if (!message || !message.content) {
+    console.error("Invalid message or content is missing");
+    return;
+  }
+
+  return new Promise((resolve) => {
+    const delay = 30; // 打字延迟时间
+    let i = 0;
+    message.displayContent = ""; // 确保开始时是空的
+
+    const typeChar = () => {
+      if (i < message.content.length) {
+        message.displayContent = message.content.substring(0, i + 1);
+        i++;
+        nextTick(() => {
+          scrollToBottom();
+          setTimeout(typeChar, delay);
+        });
+      } else {
+        resolve();
+      }
+    };
+
+    typeChar();
+  });
+};
+
+// 模拟API请求
+const mockApiRequest = async (message) => {
+  await new Promise((resolve) => setTimeout(resolve, 1000));
+  return `This is a response to: ${message}`;
+};
+
+// 会话管理
+const addNewConversation = () => {
+  conversationCounter.value++;
+  const newConversation = {
+    id: Date.now(),
+    title: `New Conversation ${conversationCounter.value}`,
+    messages: [],
+  };
+  conversations.value.push(newConversation);
+  session_id.value = ""; // 重置 session_id
+  switchConversation(newConversation.id);
+};
+
+const switchConversation = (conversationId) => {
+  currentConversationId.value = conversationId;
+  const conversation = conversations.value.find((c) => c.id === conversationId);
+  messages.value = conversation ? conversation.messages : [];
+  session_id.value = ""; // 重置 session_id
+};
+
+const toggleRightMenu = () => {
+  isRightMenuCollapsed.value = !isRightMenuCollapsed.value;
+};
+
+const handleFileDrop = async (event) => {
+  const files = Array.from(event.dataTransfer.files);
+  try {
+    isLoading.value = true;
+    const uploadPromises = files.map(async (file) => {
+      const url = await uploadFile(file);
+      return {
+        name: file.name,
+        size: file.size, // 保存原始大小(字节)
+        sizeFormatted: formatFileSize(file.size), // 格式化后的大小
+        type: file.type,
+        url: url,
+      };
+    });
+
+    const uploadedFiles = await Promise.all(uploadPromises);
+    attachedFiles.value = [...attachedFiles.value, ...uploadedFiles];
+  } catch (error) {
+    console.error("文件上传失败:", error);
+  } finally {
+    isLoading.value = false;
+  }
+};
+
+// 添加音频播放方法
+const playAudioMessage = (audioBase64) => {
+  try {
+    // 如果已经有音频在播放,先停止
+    if (audioPlayer.value) {
+      audioPlayer.value.pause();
+      audioPlayer.value = null;
+    }
+
+    // 创建新的音频元素
+    const audio = new Audio(audioBase64);
+    audioPlayer.value = audio;
+
+    // 添加事件监听
+    audio.addEventListener("ended", () => {
+      isPlaying.value = false;
+    });
+
+    audio.addEventListener("error", (e) => {
+      console.error("音频播放错误:", e);
+      isPlaying.value = false;
+    });
+
+    // 播放音频
+    isPlaying.value = true;
+    audio.play();
+  } catch (error) {
+    console.error("音频播放失败:", error);
+    isPlaying.value = false;
+  }
+};
+
+// 添加停止播放方法
+const stopAudio = () => {
+  if (audioPlayer.value) {
+    audioPlayer.value.pause();
+    audioPlayer.value = null;
+    isPlaying.value = false;
+  }
+};
+</script>
+
+<style scoped>
+.layout {
+  display: flex;
+  width: 100%;
+  height: 100vh;
+  overflow: hidden;
+}
+
+.menu {
+  background: #f7f7f8;
+  width: 260px;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  border-right: 1px solid rgba(0, 0, 0, 0.06);
+}
+
+.conversations {
+  padding: 0 12px;
+  flex: 1;
+  overflow-y: auto;
+}
+
+.chat {
+  flex: 1;
+  padding: 24px;
+  display: flex;
+  flex-direction: column;
+  gap: 24px;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
+.messages {
+  flex: 1;
+  overflow-y: auto;
+  padding-right: 16px;
+}
+
+.sender {
+  box-shadow: none;
+  border: 1px solid #e5e6eb;
+  border-radius: 8px;
+  padding: 8px;
+}
+
+.logo {
+  height: 64px;
+  display: flex;
+  align-items: center;
+  padding: 0 20px;
+}
+
+.logo-img {
+  width: 24px;
+  height: 24px;
+}
+
+.logo span {
+  margin-left: 12px;
+  font-size: 16px;
+  font-weight: 600;
+  color: rgba(0, 0, 0, 0.88);
+}
+
+.new-conversation-btn {
+  margin: 0 12px 12px;
+  height: 40px;
+  background: rgba(235, 245, 255, 0.7);
+  border: none;
+  border-radius: 8px;
+  color: #1677ff;
+  font-size: 14px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 8px;
+  cursor: pointer;
+  transition: background-color 0.2s;
+}
+
+.new-conversation-btn:hover {
+  background: rgba(235, 245, 255, 0.9);
+}
+
+.conversation-list {
+  flex: 1;
+  padding: 0 8px;
+  overflow-y: auto;
+}
+
+.conversation-item {
+  padding: 12px;
+  margin: 4px 0;
+  border-radius: 8px;
+  color: rgba(0, 0, 0, 0.88);
+  font-size: 14px;
+  cursor: pointer;
+  transition: background-color 0.2s;
+}
+
+.conversation-item:hover {
+  background: rgba(0, 0, 0, 0.04);
+}
+
+.conversation-item.active {
+  background: #fff;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
+}
+
+/* 滚动条样式 */
+.conversation-list::-webkit-scrollbar {
+  width: 4px;
+}
+
+.conversation-list::-webkit-scrollbar-thumb {
+  background: rgba(0, 0, 0, 0.15);
+  border-radius: 2px;
+}
+
+.conversation-list::-webkit-scrollbar-track {
+  background: transparent;
+}
+
+:deep(.welcome-message) {
+  background: #fff;
+  border-radius: 8px;
+  padding: 16px;
+  display: flex;
+  align-items: flex-start;
+  gap: 16px;
+}
+
+:deep(.welcome-message .title) {
+  font-size: 16px;
+  font-weight: 500;
+  color: rgba(0, 0, 0, 0.88);
+  margin-bottom: 4px;
+}
+
+:deep(.welcome-message .description) {
+  color: rgba(0, 0, 0, 0.45);
+  font-size: 14px;
+}
+
+:deep(.prompt-card) {
+  background: #f7f8fa;
+  border-radius: 8px;
+  padding: 16px;
+  margin-bottom: 12px;
+}
+
+:deep(.prompt-card-title) {
+  font-size: 16px;
+  font-weight: 500;
+  margin-bottom: 8px;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+:deep(.prompt-option) {
+  background: #f5f5f5;
+  padding: 8px 12px;
+  border-radius: 4px;
+  margin-top: 8px;
+  cursor: pointer;
+  transition: background 0.3s;
+}
+
+:deep(.prompt-option:hover) {
+  background: #e8e8e8;
+}
+
+.input-container {
+  width: 50%;
+  margin: auto auto 0;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+/* 标签栏样式 */
+.tabs-wrapper {
+  display: flex;
+  gap: 8px;
+  margin-bottom: 4px;
+}
+
+.tab-item {
+  display: flex;
+  align-items: center;
+  gap: 6px;
+  padding: 4px 12px;
+  border-radius: 4px;
+  cursor: pointer;
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.88);
+  background: transparent;
+  transition: background-color 0.2s;
+}
+
+.tab-item:hover {
+  background: rgba(0, 0, 0, 0.04);
+}
+
+.tab-item.active {
+  background: #f5f5f5;
+}
+
+.tab-item .anticon {
+  font-size: 14px;
+}
+
+.input-wrapper {
+  display: flex;
+  align-items: center;
+  padding: 8px 12px;
+  border: 1px solid #e5e6eb;
+  border-radius: 8px;
+  background: white;
+}
+
+.message-input {
+  flex: 1;
+  border: none;
+  outline: none;
+  padding: 8px;
+  font-size: 14px;
+  background: transparent;
+}
+
+.message-input::placeholder {
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.attachment-btn,
+.send-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 32px;
+  height: 32px;
+  border: none;
+  background: transparent;
+  cursor: pointer;
+  color: rgba(0, 0, 0, 0.45);
+  border-radius: 4px;
+}
+
+.attachment-btn:hover,
+.send-btn:hover {
+  background: rgba(0, 0, 0, 0.04);
+  color: rgba(0, 0, 0, 0.88);
+}
+
+.attachments-panel {
+  /* position: absolute;
+  bottom: calc(100% + 8px);
+  left: 0;
+  right: 0; */
+  background: white;
+  border-radius: 12px;
+  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+  overflow: hidden;
+}
+
+.panel-content {
+  background: white;
+}
+
+.attachments-header {
+  padding: 12px 16px;
+  border-bottom: 1px solid #f0f0f0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.attachments-header span {
+  font-size: 14px;
+  font-weight: 500;
+  color: rgba(0, 0, 0, 0.88);
+}
+
+.close-btn {
+  border: none;
+  background: transparent;
+  cursor: pointer;
+  padding: 4px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.upload-area {
+  padding: 24px;
+  /*  border: 2px dashed #d9d9d9; */
+  border-radius: 8px;
+  text-align: center;
+  cursor: pointer;
+  transition: all 0.3s;
+  /*   background: #fafafa; */
+}
+
+/* .upload-area:hover {
+  border-color: #1677ff;
+  background: #f0f7ff;
+} */
+
+.upload-area.has-files {
+  padding: 12px;
+  background: #fff;
+}
+
+.upload-icon {
+  width: 32px;
+  height: 32px;
+  margin-bottom: 8px;
+}
+
+.upload-text {
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.88);
+  margin-bottom: 4px;
+}
+
+.upload-hint {
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.file-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  padding: 10px;
+}
+
+.file-item {
+  position: relative;
+  width: 260px;
+  padding: 8px 12px;
+  margin: 4px 0;
+  background: #f5f5f5;
+  border-radius: 8px;
+}
+
+.file-item.is-image {
+  width: 68px;
+  padding: 0;
+  margin: 4px 0;
+}
+
+.file-wrapper {
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
+.file-icon {
+  width: 68px;
+  height: 68px;
+  object-fit: cover;
+  border-radius: 4px;
+}
+
+.remove-btn {
+  position: absolute;
+  top: -8px;
+  right: -8px;
+  width: 16px;
+  height: 16px;
+  padding: 0;
+  border: none;
+  background: #545454;
+  border-radius: 50%;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 14px;
+  line-height: 1;
+  color: #fff;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.remove-btn:hover {
+  opacity: 0.8;
+}
+
+.file-info {
+  margin-top: 8px;
+}
+
+.file-name {
+  display: block;
+  color: rgba(0, 0, 0, 0.88);
+  font-size: 14px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.file-size {
+  display: block;
+  color: rgba(0, 0, 0, 0.45);
+  font-size: 12px;
+  margin-top: 4px;
+}
+
+.add-more-btn {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 68px;
+  height: 68px;
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  background: transparent;
+  cursor: pointer;
+  transition: all 0.3s;
+  margin: 4px 0;
+}
+
+.add-more-btn:hover {
+  border-color: #1677ff;
+  background: #f0f7ff;
+}
+
+/* 欢迎区域样式 */
+.welcome-section {
+  max-width: 800px;
+  margin: 0 auto;
+  width: 50%;
+}
+
+.welcome-header {
+  display: flex;
+  align-items: flex-start;
+  gap: 16px;
+  margin-bottom: 24px;
+}
+
+.avatar {
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+}
+
+.welcome-info {
+  flex: 1;
+}
+
+.welcome-info h2 {
+  font-size: 20px;
+  font-weight: 500;
+  color: rgba(0, 0, 0, 0.88);
+  margin: 0 0 4px 0;
+}
+
+.welcome-info p {
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.45);
+  margin: 0;
+}
+
+.action-buttons {
+  display: flex;
+  gap: 8px;
+}
+
+.icon-btn {
+  padding: 8px;
+  border: none;
+  background: transparent;
+  cursor: pointer;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+/* 建议区域样式 */
+.suggestion-section {
+  margin-top: 32px;
+}
+
+.suggestion-section h3 {
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.45);
+  margin-bottom: 16px;
+}
+
+.cards-container {
+  display: flex;
+  gap: 16px;
+}
+
+.suggestion-card {
+  flex: 1;
+  border-radius: 8px;
+  padding: 16px;
+  background: #ffffff; /* 替换 var(--ant-color-bg-container) */
+  border-radius: 8px; /* 替换 var(--ant-border-radius-lg) */
+  transition: border 0.3s, background 0.3s; /* 替换 var(--ant-motion-duration-slow) */
+  border: 1px solid #f0f0f0; /* 替换合的 border 属性 */
+}
+:root {
+  --ant-color-bg-container: #ffffff;
+  --ant-border-radius-lg: 8px;
+  --ant-motion-duration-slow: 0.3s;
+  --ant-line-width: 1px;
+  --ant-line-type: solid;
+  --ant-color-border-secondary: #f0f0f0;
+}
+
+.card-header {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 8px;
+}
+
+.card-header h4 {
+  font-size: 16px;
+  font-weight: 500;
+  color: rgba(0, 0, 0, 0.88);
+  margin: 0;
+}
+
+.icon.red {
+  color: #ff4d4f;
+}
+
+.icon.blue {
+  color: #1890ff;
+}
+
+.card-subtitle {
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.45);
+  margin-bottom: 16px;
+}
+
+.suggestion-items {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+}
+
+.suggestion-item {
+  padding: 8px 12px;
+  background: #f5f5f5;
+  border-radius: 4px;
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.88);
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  cursor: pointer;
+}
+
+.suggestion-item:hover {
+  background: #e8e8e8;
+}
+.tab-icon {
+  width: 16px;
+}
+
+.attached-files {
+  padding: 16px;
+}
+
+.file-item {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  padding: 8px;
+  border-radius: 4px;
+}
+
+.file-item:hover {
+  background: #f5f5f5;
+}
+
+.remove-btn {
+  margin-left: auto;
+  padding: 4px;
+  border: none;
+  background: transparent;
+  cursor: pointer;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.remove-btn:hover {
+  color: rgba(0, 0, 0, 0.88);
+}
+
+.send-btn:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+.upload-area {
+  /*   border: 2px dashed #d9d9d9; */
+  border-radius: 8px;
+  padding: 20px;
+  text-align: center;
+  cursor: pointer;
+  transition: border-color 0.3s;
+}
+
+/* .upload-area:hover,
+.upload-area.drag-over {
+  border-color: #1677ff;
+} */
+
+.file-item {
+  display: flex;
+  align-items: center;
+  width: 236px;
+  height: 68px;
+  padding: 0;
+  margin: 4px 0;
+  background: #f5f5f5;
+  border-radius: 4px;
+}
+
+.file-item .remove-btn {
+  margin-left: auto;
+  padding: 4px 8px;
+  background: transparent;
+  border: none;
+  cursor: pointer;
+}
+
+/* 聊天内容显示框 */
+.message-list {
+  width: 60%;
+  overflow-y: auto;
+  overflow-x: hidden;
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+  margin: 0 auto;
+}
+
+.message-item {
+  display: flex;
+  margin-bottom: 12px;
+}
+
+/* 用户消息靠右显示 */
+.user {
+  display: flex;
+  justify-content: flex-end;
+}
+
+.message-bubble {
+  max-width: 80%;
+  padding: 12px 16px;
+  border-radius: 12px;
+  background: #fff;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+}
+
+/* 用户消息的气泡样式 */
+.user .message-bubble {
+  background: #fff;
+  color: rgba(0, 0, 0, 0.88);
+}
+
+/* AI消息的气泡样式 */
+.assistant .message-bubble {
+  background: #f7f7f8;
+  color: rgba(0, 0, 0, 0.88);
+}
+
+.message-content {
+  font-size: 14px;
+  line-height: 1.5;
+  white-space: pre-wrap;
+  word-break: break-word;
+}
+
+.right_menu {
+  width: 300px;
+  height: 100%;
+  background: #f7f7f8;
+  border-left: 1px solid rgba(0, 0, 0, 0.06);
+  position: relative;
+  transition: width 0.3s ease;
+}
+
+.right_menu.collapsed {
+  width: 0px;
+}
+
+.collapse-button {
+  position: absolute;
+  left: -20px;
+  top: 50%;
+  transform: translateY(-50%);
+  width: 20px;
+  height: 40px;
+  background: #fff;
+  border: 1px solid rgba(0, 0, 0, 0.06);
+  border-right: none;
+  border-radius: 4px 0 0 4px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  z-index: 1;
+}
+
+.collapse-button:hover {
+  background: #f5f5f5;
+}
+
+.collapse-icon {
+  width: 12px;
+  height: 12px;
+}
+
+.right-menu-content {
+  padding: 20px;
+  overflow: hidden;
+}
+
+.right_menu.collapsed .right-menu-content {
+  display: none;
+}
+
+/* 添加打字效果的光标样式 */
+/* .assistant .message-content {
+  position: relative;
+} 
+
+.assistant .message-content::after {
+  content: '|';
+  position: absolute;
+  margin-left: 2px;
+  animation: cursor-blink 1s infinite;
+}
+
+@keyframes cursor-blink {
+  0%, 100% { opacity: 1; }
+  50% { opacity: 0; }
+}*/
+
+.file-message {
+  background: #f7f7f8;
+  border-radius: 8px;
+  padding: 12px;
+  margin-bottom: 8px;
+}
+
+.file-info-display {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 8px;
+}
+
+.file-type-icon {
+  width: 24px;
+  height: 24px;
+  object-fit: contain;
+}
+
+.file-details {
+  display: flex;
+  flex-direction: column;
+}
+
+.file-name {
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.88);
+  margin-bottom: 4px;
+}
+
+.file-size {
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.file-action {
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+  padding-left: 36px;
+}
+
+/* 修改用户消息气泡样式,确保文件信息正确显示 */
+.user .message-bubble {
+  background: #fff;
+  color: rgba(0, 0, 0, 0.88);
+}
+
+.user .file-message {
+  background: #f7f7f8;
+}
+
+/* 图片预览样式 */
+.image-preview {
+  position: relative;
+  width: 200px;
+  border-radius: 8px;
+  overflow: hidden;
+  background: #fff;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+}
+
+.preview-image {
+  width: 100%;
+  height: auto;
+  display: block;
+}
+
+.image-info {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 8px;
+  background: rgba(255, 255, 255, 0.9);
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+  text-align: center;
+  border-top: 1px solid rgba(0, 0, 0, 0.06);
+}
+
+/* 文档显示样式保持不变 */
+.file-info-display {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin-bottom: 8px;
+}
+
+.file-type-icon {
+  width: 24px;
+  height: 24px;
+  object-fit: contain;
+}
+
+.file-details {
+  display: flex;
+  flex-direction: column;
+}
+
+.file-name {
+  font-size: 14px;
+  color: rgba(0, 0, 0, 0.88);
+  margin-bottom: 4px;
+}
+
+.file-size {
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+}
+
+.file-action {
+  font-size: 12px;
+  color: rgba(0, 0, 0, 0.45);
+  padding-left: 36px;
+}
+
+/* 添加音频控制相关样式 */
+.audio-controls {
+  margin-top: 8px;
+  display: flex;
+  align-items: center;
+}
+
+.audio-btn {
+  background: transparent;
+  border: none;
+  padding: 4px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 50%;
+  transition: background-color 0.2s;
+}
+
+.audio-btn:hover {
+  background: rgba(0, 0, 0, 0.04);
+}
+
+.audio-icon {
+  width: 20px;
+  height: 20px;
+}
+
+/* 音频波形动画样式 */
+.wave-animation {
+  display: flex;
+  align-items: center;
+  height: 20px;
+  gap: 2px;
+}
+
+.wave-bar {
+  display: inline-block;
+  width: 3px;
+  height: 100%;
+  background: #1677ff;
+  border-radius: 1px;
+  animation: wave 1s ease-in-out infinite;
+}
+
+.wave-bar:nth-child(1) {
+  animation-delay: 0s;
+}
+
+.wave-bar:nth-child(2) {
+  animation-delay: 0.2s;
+}
+
+.wave-bar:nth-child(3) {
+  animation-delay: 0.4s;
+}
+
+.wave-bar:nth-child(4) {
+  animation-delay: 0.6s;
+}
+
+@keyframes wave {
+  0%,
+  100% {
+    height: 6px;
+  }
+  50% {
+    height: 18px;
+  }
+}
+
+.audio-btn {
+  background: transparent;
+  border: none;
+  padding: 4px 8px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 4px;
+  transition: background-color 0.2s;
+  min-width: 32px;
+  height: 32px;
+}
+
+.audio-btn:hover {
+  background: rgba(0, 0, 0, 0.04);
+}
+
+.audio-icon {
+  width: 20px;
+  height: 20px;
+}
+</style>

+ 21 - 0
src/views/user.vue

@@ -0,0 +1,21 @@
+<template>
+  <div>
+    配置中心
+  </div>
+</template>
+
+<script>
+export default {
+  components: {},
+  data () {
+    return {
+    }
+  },
+  computed: {},
+  created () {},
+  methods: {}
+}
+</script>
+
+<style scoped>
+</style>

+ 66 - 0
vite.config.js

@@ -0,0 +1,66 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import vueJsx from '@vitejs/plugin-vue-jsx'
+import path from 'path'
+import { fileURLToPath } from 'url'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+
+function resolve(dir) {
+    return path.join(__dirname, dir)
+}
+
+export default defineConfig({
+    plugins: [
+        vue(),
+        vueJsx()
+    ],
+    define: {
+        'process.env': {
+            BASE_URL: '/',
+            NODE_ENV: process.env.NODE_ENV,
+            API_URL: 'http://your-api-url',
+            // ... 其他环境变量
+        }
+    },
+    css: {
+        preprocessorOptions: {
+            less: {
+                javascriptEnabled: true,
+                additionalData: `@import "${path.resolve(__dirname, 'src/app.less')}";`
+            }
+        }
+    },
+    resolve: {
+        alias: {
+            '@': resolve('src'),
+            '@api': resolve('src/api'),
+            '@assets': resolve('src/assets'),
+            '@layouts': resolve('src/layouts'),
+            '@static': resolve('src/static'),
+            '@components': resolve('src/components'),
+            '@router': resolve('src/router'),
+            '@utils': resolve('src/utils'),
+            '@store': resolve('src/store'),
+            '@views': resolve('src/views')
+        },
+        extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.vue']
+    },
+    server: {
+        port: 8003,
+        proxy: {
+            '/WebSocketConfig/': {
+                target: 'ws://test.com',
+                changeOrigin: true,
+                ws: true,
+            },
+            '/api/': {
+                target: 'http://test.com',
+                changeOrigin: true,
+                ws: false,
+            }
+        }
+    },
+    productionSourceMap: false,
+    lintOnSave: false,
+})

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.