7746 2 недель назад
Сommit
89e89bf00f
58 измененных файлов с 6249 добавлено и 0 удалено
  1. 5 0
      .env.development
  2. 5 0
      .env.production
  3. 24 0
      .gitignore
  4. 3 0
      .vscode/extensions.json
  5. 5 0
      README.md
  6. 13 0
      index.html
  7. 12 0
      jsconfig.json
  8. 2511 0
      package-lock.json
  9. 27 0
      package.json
  10. BIN
      public/logo.png
  11. 52 0
      src/App.vue
  12. 9 0
      src/api/category.js
  13. 22 0
      src/api/common.js
  14. 30 0
      src/api/goods.js
  15. 0 0
      src/api/home.js
  16. 13 0
      src/api/user.js
  17. BIN
      src/assets/images/excel.png
  18. BIN
      src/assets/images/logo.png
  19. BIN
      src/assets/images/pdf.png
  20. BIN
      src/assets/images/ppt.png
  21. BIN
      src/assets/images/video.jpeg
  22. BIN
      src/assets/images/weixin.png
  23. BIN
      src/assets/images/word.png
  24. BIN
      src/assets/images/zip.jpeg
  25. 22 0
      src/components/logo/index.vue
  26. 130 0
      src/components/viewFile/index.vue
  27. 51 0
      src/main.js
  28. 84 0
      src/pages/category/components/GridItem.vue
  29. 71 0
      src/pages/category/components/Left.vue
  30. 166 0
      src/pages/category/components/Right.vue
  31. 134 0
      src/pages/category/index.vue
  32. 280 0
      src/pages/goods/index.vue
  33. 78 0
      src/pages/home/components/AccountModal.vue
  34. 42 0
      src/pages/home/components/Category.vue
  35. 169 0
      src/pages/home/components/HandleBar.vue
  36. 17 0
      src/pages/home/components/Notice.vue
  37. 51 0
      src/pages/home/components/Toolbar.vue
  38. 113 0
      src/pages/home/index.vue
  39. 157 0
      src/pages/login/index.vue
  40. 148 0
      src/pages/register/index.vue
  41. 139 0
      src/reset.css
  42. 57 0
      src/router/index.js
  43. 16 0
      src/store/category.js
  44. 53 0
      src/store/goods.js
  45. 19 0
      src/store/home.js
  46. 4 0
      src/store/index.js
  47. 18 0
      src/store/user.js
  48. 21 0
      src/styles/common.less
  49. 4 0
      src/styles/variables.less
  50. 38 0
      src/utils/antd-locale-config.js
  51. 122 0
      src/utils/antd-processor.js
  52. 69 0
      src/utils/init-processor.js
  53. 778 0
      src/utils/mock.js
  54. 88 0
      src/utils/request.js
  55. 224 0
      src/utils/text-processor.js
  56. 12 0
      src/utils/token.js
  57. 96 0
      src/utils/vue3-plugin.js
  58. 47 0
      vite.config.js

+ 5 - 0
.env.development

@@ -0,0 +1,5 @@
+ENV = 'development'
+
+VITE_APP_BASE_API = 'https://jiasm.zfire.top'
+
+VITE_APP_BASE_OSS = 'https://jiasm.zfire.top/overseas-api/img/get?key='

+ 5 - 0
.env.production

@@ -0,0 +1,5 @@
+ENV = 'development'
+
+VITE_APP_BASE_API = 'https://jiasm.zfire.top'
+
+VITE_APP_BASE_OSS = 'https://jiasm.zfire.top/overseas-api/img/get?key='

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

+ 13 - 0
index.html

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

+ 12 - 0
jsconfig.json

@@ -0,0 +1,12 @@
+{
+  "compilerOptions": {
+    "target": "ES6",
+    "module": "commonjs",
+    "allowSyntheticDefaultImports": true,
+  "baseUrl": "./",
+  "paths": {
+    "@/*": ["src/*"]
+  }
+},
+"exclude": ["node_modules"]
+}

+ 2511 - 0
package-lock.json

@@ -0,0 +1,2511 @@
+{
+  "name": "my-shop-app",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "my-shop-app",
+      "version": "0.0.0",
+      "dependencies": {
+        "@ant-design/icons-vue": "^7.0.1",
+        "ant-design-vue": "^4.2.6",
+        "axios": "^1.13.2",
+        "js-perform-lock": "^1.0.5",
+        "lodash-es": "^4.17.21",
+        "pinia": "^3.0.4",
+        "vue": "^3.5.24",
+        "vue-router": "^4.6.3"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^6.0.1",
+        "less": "^4.4.2",
+        "unplugin-vue-components": "^30.0.0",
+        "vite": "^7.2.4"
+      }
+    },
+    "node_modules/@ant-design/colors": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz",
+      "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@ctrl/tinycolor": "^3.4.0"
+      }
+    },
+    "node_modules/@ant-design/icons-svg": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
+      "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
+      "license": "MIT"
+    },
+    "node_modules/@ant-design/icons-vue": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz",
+      "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-svg": "^4.2.1"
+      },
+      "peerDependencies": {
+        "vue": ">=3.0.3"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+      "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+      "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+      "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.5"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+      "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+      "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.28.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
+      "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@emotion/hash": {
+      "version": "0.9.2",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
+      "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
+      "license": "MIT"
+    },
+    "node_modules/@emotion/unitless": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
+      "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
+      "license": "MIT"
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+      "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+      "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+      "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+      "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+      "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+      "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+      "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+      "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+      "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+      "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+      "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+      "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+      "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+      "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+      "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+      "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+      "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+      "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+      "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+      "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+      "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/remapping": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-beta.50",
+      "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz",
+      "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
+      "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
+      "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
+      "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
+      "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
+      "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
+      "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
+      "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
+      "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
+      "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
+      "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
+      "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
+      "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
+      "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
+      "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
+      "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz",
+      "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
+      "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
+      "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
+      "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+      "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@simonwep/pickr": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz",
+      "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==",
+      "license": "MIT",
+      "dependencies": {
+        "core-js": "^3.15.1",
+        "nanopop": "^2.1.0"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz",
+      "integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@rolldown/pluginutils": "1.0.0-beta.50"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz",
+      "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/shared": "3.5.24",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz",
+      "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-core": "3.5.24",
+        "@vue/shared": "3.5.24"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz",
+      "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@vue/compiler-core": "3.5.24",
+        "@vue/compiler-dom": "3.5.24",
+        "@vue/compiler-ssr": "3.5.24",
+        "@vue/shared": "3.5.24",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.21",
+        "postcss": "^8.5.6",
+        "source-map-js": "^1.2.1"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz",
+      "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.24",
+        "@vue/shared": "3.5.24"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.4",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+      "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+      "license": "MIT"
+    },
+    "node_modules/@vue/devtools-kit": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
+      "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-shared": "^7.7.9",
+        "birpc": "^2.3.0",
+        "hookable": "^5.5.3",
+        "mitt": "^3.0.1",
+        "perfect-debounce": "^1.0.0",
+        "speakingurl": "^14.0.1",
+        "superjson": "^2.2.2"
+      }
+    },
+    "node_modules/@vue/devtools-shared": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
+      "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
+      "license": "MIT",
+      "dependencies": {
+        "rfdc": "^1.4.1"
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz",
+      "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/shared": "3.5.24"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz",
+      "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.24",
+        "@vue/shared": "3.5.24"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz",
+      "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/reactivity": "3.5.24",
+        "@vue/runtime-core": "3.5.24",
+        "@vue/shared": "3.5.24",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz",
+      "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.5.24",
+        "@vue/shared": "3.5.24"
+      },
+      "peerDependencies": {
+        "vue": "3.5.24"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.5.24",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz",
+      "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==",
+      "license": "MIT"
+    },
+    "node_modules/acorn": {
+      "version": "8.15.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+      "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/ant-design-vue": {
+      "version": "4.2.6",
+      "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-4.2.6.tgz",
+      "integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-vue": "^7.0.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/array-tree-filter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
+      "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==",
+      "license": "MIT"
+    },
+    "node_modules/async-validator": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
+      "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/birpc": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.8.0.tgz",
+      "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/compute-scroll-into-view": {
+      "version": "1.0.20",
+      "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
+      "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==",
+      "license": "MIT"
+    },
+    "node_modules/confbox": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
+      "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/copy-anything": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+      "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-what": "^3.14.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/core-js": {
+      "version": "3.47.0",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz",
+      "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "license": "MIT"
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.19",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
+      "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "license": "MIT",
+      "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.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/dom-align": {
+      "version": "1.12.4",
+      "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz",
+      "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==",
+      "license": "MIT"
+    },
+    "node_modules/dom-scroll-into-view": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz",
+      "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==",
+      "license": "MIT"
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/errno": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+      "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "prr": "~1.0.1"
+      },
+      "bin": {
+        "errno": "cli.js"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+      "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.12",
+        "@esbuild/android-arm": "0.25.12",
+        "@esbuild/android-arm64": "0.25.12",
+        "@esbuild/android-x64": "0.25.12",
+        "@esbuild/darwin-arm64": "0.25.12",
+        "@esbuild/darwin-x64": "0.25.12",
+        "@esbuild/freebsd-arm64": "0.25.12",
+        "@esbuild/freebsd-x64": "0.25.12",
+        "@esbuild/linux-arm": "0.25.12",
+        "@esbuild/linux-arm64": "0.25.12",
+        "@esbuild/linux-ia32": "0.25.12",
+        "@esbuild/linux-loong64": "0.25.12",
+        "@esbuild/linux-mips64el": "0.25.12",
+        "@esbuild/linux-ppc64": "0.25.12",
+        "@esbuild/linux-riscv64": "0.25.12",
+        "@esbuild/linux-s390x": "0.25.12",
+        "@esbuild/linux-x64": "0.25.12",
+        "@esbuild/netbsd-arm64": "0.25.12",
+        "@esbuild/netbsd-x64": "0.25.12",
+        "@esbuild/openbsd-arm64": "0.25.12",
+        "@esbuild/openbsd-x64": "0.25.12",
+        "@esbuild/openharmony-arm64": "0.25.12",
+        "@esbuild/sunos-x64": "0.25.12",
+        "@esbuild/win32-arm64": "0.25.12",
+        "@esbuild/win32-ia32": "0.25.12",
+        "@esbuild/win32-x64": "0.25.12"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+      "license": "MIT"
+    },
+    "node_modules/exsolve": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
+      "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
+      "license": "ISC",
+      "optional": true
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hookable": {
+      "version": "5.5.3",
+      "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+      "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+      "license": "MIT"
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "dev": true,
+      "license": "MIT",
+      "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.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+      "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "image-size": "bin/image-size.js"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-plain-object": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
+      "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-what": {
+      "version": "3.14.1",
+      "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+      "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/js-perform-lock": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/js-perform-lock/-/js-perform-lock-1.0.5.tgz",
+      "integrity": "sha512-aDp38fxLnur2YMsJhw1Ab23PdwceXMU5Pkcb4R5MSaRfySiDwbDR2L/ysnB4X7/1XERkLC56r32qwtNlitBSGg==",
+      "license": "ISC"
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "license": "MIT"
+    },
+    "node_modules/less": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/less/-/less-4.4.2.tgz",
+      "integrity": "sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "copy-anything": "^2.0.1",
+        "parse-node-version": "^1.0.1",
+        "tslib": "^2.3.0"
+      },
+      "bin": {
+        "lessc": "bin/lessc"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "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/local-pkg": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
+      "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mlly": "^1.7.4",
+        "pkg-types": "^2.3.0",
+        "quansync": "^0.2.11"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "pify": "^4.0.1",
+        "semver": "^5.6.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mime": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mitt": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+      "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+      "license": "MIT"
+    },
+    "node_modules/mlly": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+      "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "pathe": "^2.0.3",
+        "pkg-types": "^1.3.1",
+        "ufo": "^1.6.1"
+      }
+    },
+    "node_modules/mlly/node_modules/confbox": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+      "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/mlly/node_modules/pkg-types": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+      "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.1.8",
+        "mlly": "^1.7.4",
+        "pathe": "^2.0.1"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "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.npmjs.org/nanopop/-/nanopop-2.4.2.tgz",
+      "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==",
+      "license": "MIT"
+    },
+    "node_modules/needle": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz",
+      "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "iconv-lite": "^0.6.3",
+        "sax": "^1.2.4"
+      },
+      "bin": {
+        "needle": "bin/needle"
+      },
+      "engines": {
+        "node": ">= 4.4.x"
+      }
+    },
+    "node_modules/parse-node-version": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/perfect-debounce": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+      "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+      "license": "MIT"
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+      "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/pify": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+      "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pinia": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
+      "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^7.7.7"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.5.0",
+        "vue": "^3.5.11"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/pinia/node_modules/@vue/devtools-api": {
+      "version": "7.7.9",
+      "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
+      "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-kit": "^7.7.9"
+      }
+    },
+    "node_modules/pkg-types": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
+      "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.2.2",
+        "exsolve": "^1.0.7",
+        "pathe": "^2.0.3"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "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"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "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.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/prr": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/quansync": {
+      "version": "0.2.11",
+      "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
+      "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/antfu"
+        },
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/sxzz"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+      "license": "MIT"
+    },
+    "node_modules/rfdc": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+      "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+      "license": "MIT"
+    },
+    "node_modules/rollup": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz",
+      "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.53.3",
+        "@rollup/rollup-android-arm64": "4.53.3",
+        "@rollup/rollup-darwin-arm64": "4.53.3",
+        "@rollup/rollup-darwin-x64": "4.53.3",
+        "@rollup/rollup-freebsd-arm64": "4.53.3",
+        "@rollup/rollup-freebsd-x64": "4.53.3",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+        "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+        "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+        "@rollup/rollup-linux-arm64-musl": "4.53.3",
+        "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+        "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+        "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-musl": "4.53.3",
+        "@rollup/rollup-openharmony-arm64": "4.53.3",
+        "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+        "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+        "@rollup/rollup-win32-x64-gnu": "4.53.3",
+        "@rollup/rollup-win32-x64-msvc": "4.53.3",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/sax": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
+      "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "optional": true
+    },
+    "node_modules/scroll-into-view-if-needed": {
+      "version": "2.2.31",
+      "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
+      "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
+      "license": "MIT",
+      "dependencies": {
+        "compute-scroll-into-view": "^1.0.20"
+      }
+    },
+    "node_modules/semver": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+      "dev": true,
+      "license": "ISC",
+      "optional": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/shallow-equal": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
+      "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==",
+      "license": "MIT"
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/speakingurl": {
+      "version": "14.0.1",
+      "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+      "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stylis": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
+      "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
+      "license": "MIT"
+    },
+    "node_modules/superjson": {
+      "version": "2.2.5",
+      "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.5.tgz",
+      "integrity": "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==",
+      "license": "MIT",
+      "dependencies": {
+        "copy-anything": "^4"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/superjson/node_modules/copy-anything": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
+      "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+      "license": "MIT",
+      "dependencies": {
+        "is-what": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/superjson/node_modules/is-what": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
+      "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/mesqueeb"
+      }
+    },
+    "node_modules/throttle-debounce": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
+      "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.22"
+      }
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.15",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+      "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "dev": true,
+      "license": "0BSD"
+    },
+    "node_modules/ufo": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
+      "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/unplugin": {
+      "version": "2.3.10",
+      "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz",
+      "integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/remapping": "^2.3.5",
+        "acorn": "^8.15.0",
+        "picomatch": "^4.0.3",
+        "webpack-virtual-modules": "^0.6.2"
+      },
+      "engines": {
+        "node": ">=18.12.0"
+      }
+    },
+    "node_modules/unplugin-utils": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+      "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pathe": "^2.0.3",
+        "picomatch": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=20.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sxzz"
+      }
+    },
+    "node_modules/unplugin-vue-components": {
+      "version": "30.0.0",
+      "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz",
+      "integrity": "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chokidar": "^4.0.3",
+        "debug": "^4.4.3",
+        "local-pkg": "^1.1.2",
+        "magic-string": "^0.30.19",
+        "mlly": "^1.8.0",
+        "tinyglobby": "^0.2.15",
+        "unplugin": "^2.3.10",
+        "unplugin-utils": "^0.3.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@babel/parser": "^7.15.8",
+        "@nuxt/kit": "^3.2.2 || ^4.0.0",
+        "vue": "2 || 3"
+      },
+      "peerDependenciesMeta": {
+        "@babel/parser": {
+          "optional": true
+        },
+        "@nuxt/kit": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite": {
+      "version": "7.2.4",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz",
+      "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.25.0",
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.3",
+        "postcss": "^8.5.6",
+        "rollup": "^4.43.0",
+        "tinyglobby": "^0.2.15"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^20.19.0 || >=22.12.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^20.19.0 || >=22.12.0",
+        "jiti": ">=1.21.0",
+        "less": "^4.0.0",
+        "lightningcss": "^1.21.0",
+        "sass": "^1.70.0",
+        "sass-embedded": "^1.70.0",
+        "stylus": ">=0.54.8",
+        "sugarss": "^5.0.0",
+        "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.24",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz",
+      "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/compiler-dom": "3.5.24",
+        "@vue/compiler-sfc": "3.5.24",
+        "@vue/runtime-dom": "3.5.24",
+        "@vue/server-renderer": "3.5.24",
+        "@vue/shared": "3.5.24"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-router": {
+      "version": "4.6.3",
+      "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz",
+      "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vue/devtools-api": "^6.6.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.0"
+      }
+    },
+    "node_modules/vue-types": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz",
+      "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==",
+      "license": "MIT",
+      "dependencies": {
+        "is-plain-object": "3.0.1"
+      },
+      "engines": {
+        "node": ">=10.15.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
+    "node_modules/warning": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+      "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.0.0"
+      }
+    },
+    "node_modules/webpack-virtual-modules": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
+      "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
+      "dev": true,
+      "license": "MIT"
+    }
+  }
+}

+ 27 - 0
package.json

@@ -0,0 +1,27 @@
+{
+  "name": "my-shop-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.2.6",
+    "axios": "^1.13.2",
+    "js-perform-lock": "^1.0.5",
+    "lodash-es": "^4.17.21",
+    "pinia": "^3.0.4",
+    "vue": "^3.5.24",
+    "vue-router": "^4.6.3"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^6.0.1",
+    "less": "^4.4.2",
+    "unplugin-vue-components": "^30.0.0",
+    "vite": "^7.2.4"
+  }
+}

BIN
public/logo.png


+ 52 - 0
src/App.vue

@@ -0,0 +1,52 @@
+<template>
+  <a-config-provider :theme="themeConfig" :locale="antdLocale">
+    <div id="app">
+      <div v-translate>
+        <router-view />
+      </div>
+    </div>
+  </a-config-provider>
+</template>
+
+<script setup lang="js">
+import { computed, onMounted, nextTick, provide } from 'vue';
+import { useTextProcessor } from './utils/vue3-plugin'
+
+const { processTextNodes, processAntdComponents } = useTextProcessor()
+
+import { getAntdLocale } from './utils/antd-locale-config'
+const themeConfig = {
+  token: {
+    // 主题颜色
+    colorPrimary: '#1677ff',
+    borderRadius: 2
+  }
+}
+
+// Ant Design 国际化
+const antdLocale = computed(() => {
+  const language = window.Vue_Translation_Of_Text_Type || 'zh'
+  return getAntdLocale(language)
+})
+
+// 提供 locale 给子组件
+provide('antdLocale', antdLocale)
+
+onMounted(() => {
+  nextTick(() => {
+    processTextNodes()
+    processAntdComponents()
+  })
+})
+</script>
+
+<style lang="less">
+@import './styles/variables.less';
+@import './styles/common.less';
+
+#app {
+  font-size: 14px;
+  min-height: 100vh;
+  background: #f2f2f2;
+}
+</style>

+ 9 - 0
src/api/category.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request';
+
+// 获取商品分类
+export const getList = () => request({
+  // url: '/overseas-miniapp/goods/category/list?type=1',
+  url: '/overseas-api/goods/category/list?type=1',
+  method: 'get',
+  params: {}
+})

+ 22 - 0
src/api/common.js

@@ -0,0 +1,22 @@
+import request from '@/utils/request';
+
+// 顶部公告
+export const getNotice = () => request({
+  url: '/overseas-miniapp/shpping/cart/notice',
+  method: 'get',
+  params: {}
+})
+
+// 获取仓库列表
+export const getStoreList = (params = {}) => request({
+  url: '/overseas-miniapp/storage/locate/list',
+  method: 'get',
+  params
+})
+
+// 语言转换
+export const transformLanguageText = params => request({
+  url: '/translate/api/v1/common/readTranslationOfText',
+  method: 'post',
+  params: params
+})

+ 30 - 0
src/api/goods.js

@@ -0,0 +1,30 @@
+import request from '@/utils/request'
+
+// 获取商品列表
+export const getList = params => request({
+  url: '/overseas-miniapp/goods/list/sort/page',
+  method: 'get',
+  params: params
+})
+
+// 获取商品详情
+export const getDetail = goodsId => request({
+  url: `/overseas-api/goods/detail?goodsId=${goodsId}`,
+  method: 'get',
+  params: {}
+})
+
+// 获取商品文档列表
+export const getFileList = goodsId => request({
+  url: `/overseas-api/goods/documents?goodsId=${goodsId}`,
+  method: 'post',
+  data: {}
+})
+
+// 加入购物车
+export const addToCard = params => request({
+  url: '/overseas-miniapp/shpping/cart/add/one',
+  method: 'post',
+  data: params,
+  json: true
+})

+ 0 - 0
src/api/home.js


+ 13 - 0
src/api/user.js

@@ -0,0 +1,13 @@
+import request from '@/utils/request'
+
+export const getCode = () => request({
+  url: '/overseas-api/admin/user/imageVerification',
+  method: 'get',
+  params: {}
+})
+
+export const userLogin = params => request({
+  url: '/overseas-api/admin/user/login',
+  method: 'post',
+  params: params
+})

BIN
src/assets/images/excel.png


BIN
src/assets/images/logo.png


BIN
src/assets/images/pdf.png


BIN
src/assets/images/ppt.png


BIN
src/assets/images/video.jpeg


BIN
src/assets/images/weixin.png


BIN
src/assets/images/word.png


BIN
src/assets/images/zip.jpeg


+ 22 - 0
src/components/logo/index.vue

@@ -0,0 +1,22 @@
+<template>
+  <img class="logo-image" :src="Logo" alt="logo" />
+</template>
+
+<script setup lang="js">
+import Logo from '@/assets/images/logo.png'
+
+const props = defineProps({
+  size: {
+    type: String,
+    default: '100%'
+  }
+})
+</script>
+
+<style lang="less" scoped>
+.logo-image {
+  width: 100%;
+  height: 100%;
+  object-fit: contain;
+}
+</style>

+ 130 - 0
src/components/viewFile/index.vue

@@ -0,0 +1,130 @@
+<template>
+  <div class="view-file">
+    <div class="thumb-image">
+      <img v-if="thumbImage == 'word'" class="file" src="@/assets/images/word.png" />
+      <img v-if="thumbImage == 'excel'" class="file" src="@/assets/images/excel.png" />
+      <img v-if="thumbImage == 'ppt'" class="file" src="@/assets/images/ppt.png" />
+      <img v-if="thumbImage == 'pdf'" class="file" src="@/assets/images/pdf.png" />
+      <img v-if="thumbImage == 'file'" class="file" src="@/assets/images/zip.jpeg" />
+      <img v-if="thumbImage == 'video'" class="file" src="@/assets/images/video.jpeg" />
+    </div>
+    <div class="info-content">
+      <div class="name">{{ name }}</div>
+      <div v-if="showContent" class="handle-content">
+        <a-button v-if="showView" type="link" @click.stop="viewFile">查看</a-button>
+        <a-button v-if="showDownload" type="link" @click.stop="downloadFile">下载</a-button>
+        <a-button v-if="showDelete" type="link" @click.stop="deleteFile">删除</a-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import { computed } from 'vue';
+
+const props = defineProps({
+  url: {
+    type: String,
+    default: ''
+  },
+  name: {
+    type: String,
+    default: ''
+  },
+  showView: {
+    type: Boolean,
+    default: true
+  },
+  showDownload: {
+    type: Boolean,
+    default: true
+  },
+  showDelete: {
+    type: Boolean,
+    default: true
+  }
+})
+
+const emits = defineEmits(['view-file', 'download-file', 'delete-file']);
+
+const showContent = computed(() => {
+  return props.showView || props.showDownload || props.showDelete;
+})
+
+const thumbImage = computed(() => {
+  if (!props.url) return;
+  const fileSuffix = props.url.substring(props.url.lastIndexOf('.') + 1)
+  if (['jpg', 'jpeg', 'png'].includes(fileSuffix)) {
+    return 'image'
+  } else if (['doc', 'docx', 'dot', 'wps', 'wpt'].includes(fileSuffix)) {
+    return 'word'
+  } else if (['xls', 'xlsx', 'xlt', 'et', 'ett'].includes(fileSuffix)) {
+    return 'excel'
+  } else if (['ppt', 'pptx', 'dps', 'dpt', 'pot', 'pps'].includes(fileSuffix)) {
+    return 'ppt'
+  } else if (['pdf'].includes(fileSuffix)) {
+    return 'pdf'
+  } else if (['zip', 'rar', 'gz', 'apk'].includes(fileSuffix)) {
+    return 'file'
+  } else if (['mp4'].includes(fileSuffix)) {
+    return 'video'
+  }
+  return '';
+});
+
+const viewFile = () => {
+  if (!props.url) return;
+  emits('view-file', props.url);
+  window.open(props.url, '_blank');
+}
+
+const downloadFile = () => {
+  if (!props.url) return;
+  emits('download-file', props.url);
+  const a = document.createElement('a');
+  a.href = props.url;
+  a.target = '_blank';
+  a.download = props.name ? `${props.name}.pdf` : new Date.getTime() + '.pdf';
+  a.click();
+}
+
+const deleteFile = () => {
+  emit('delete-file', props.url);
+}
+
+</script>
+
+<style lang="less" scoped>
+.view-file {
+  width: 100%;
+  display: flex;
+  .thumb-image {
+    width: 32px;
+    height: 32px;
+    flex-shrink: 0;
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  .info-content {
+    flex: 1;
+    display: flex;
+    min-width: 0;
+    align-items: center;
+    white-space: wrap;
+    margin-left: 10px;
+    .name {
+      flex: 1;
+      min-width: 1px;
+      color: @theme-color;
+      cursor: pointer;
+      white-space: wrap;
+    }
+    .handle-content {
+      flex-shrink: 0;
+      margin-left: 15px;
+    }
+  }
+}
+</style>

+ 51 - 0
src/main.js

@@ -0,0 +1,51 @@
+import { createApp } from 'vue'
+import './reset.css'
+import App from './App.vue'
+import router from './router'
+import store from './store'
+import Antd from 'ant-design-vue'
+import 'ant-design-vue/dist/reset.css'
+import { ConfigProvider } from 'ant-design-vue'
+import {
+  GlobalTextProcessor,
+  translaBeforeRegistration,
+  setupAntdLocale 
+} from './utils/init-processor'
+
+const app = createApp(App)
+
+// 使用 Ant Design
+app.use(Antd)
+
+// 初始化翻译
+translaBeforeRegistration(() => {
+  const language = window.Vue_Translation_Of_Text_Type || 'zh'
+  
+  // 设置 Ant Design 国际化
+  // setupAntdLocale(app, language)
+  const locale = setupAntdLocale(app, language)
+  
+  // 使用 ConfigProvider 包装应用
+  const AppWithConfig = {
+    components: { App },
+    setup() {
+      provide('antdLocale', locale)
+      return () => h(ConfigProvider, { locale }, () => h(App))
+    }
+  }
+  
+  // 注册全局文本处理器
+  app.use(GlobalTextProcessor)
+
+  app.use(router)
+  app.use(store)
+  
+  app.mount('#app')
+  
+  // 初始处理一次
+  setTimeout(() => {
+    if (app.config.globalProperties.$processAntdComponents) {
+      app.config.globalProperties.$processAntdComponents()
+    }
+  }, 100)
+})

+ 84 - 0
src/pages/category/components/GridItem.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="grid-item">
+    <div class="image-box">
+      <img :src="item.imgUrl" />
+    </div>
+    <div class="grid-item__content">
+      <div class="item-name" @click="viewDetail">{{ item.goodsName }}</div>
+      <div v-if="!isLogin" class="link-box">
+        <a-button type="link" @click="toLogin">登录</a-button>
+        <span>查看价格</span>
+      </div>
+      <div v-else class="link-box">价格: ¥ {{ item.goodsPrice }}</div>
+      <div class="item-handle__box">
+        <a-flex justify="space-between">
+          <a-button :disabled="!isLogin" type="primary" @click="handleAddToCart">添加到购物车</a-button>
+          <a-button :disabled="!isLogin" type="primary">添加到列表</a-button>
+        </a-flex>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+const props = defineProps({
+  isLogin: {
+    type: Boolean,
+    default: false
+  },
+  item: {
+    type: Object,
+    default: () => ({})
+  }
+})
+
+const emits = defineEmits(['add-to-cart', 'goods-detail', 'to-login']);
+
+const handleAddToCart = () => {
+  emits('add-to-cart', props.item)
+}
+
+const viewDetail = () => {
+  emits('goods-detail', props.item)
+}
+
+const toLogin = () => {
+  emits('to-login')
+}
+</script>
+
+<style lang="less" scoped>
+.grid-item {
+  border-radius: 2px;
+  border: 1px solid @border-color;
+  .image-box {
+    img {
+      display: block;
+      max-width: 100%;
+      aspect-ratio: 1/1;
+      object-fit: contain;
+    }
+  }
+ .grid-item__content {
+  padding: 0 12px;
+  .item-name {
+    height: 63px;
+    margin-top: 10px;
+    margin-bottom: 10px;
+    color: @theme-color;
+    font-size: 14px;
+    cursor: pointer;
+    line-height: 1.5;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    -webkit-line-clamp: 3;
+  }
+  .link-box {
+    margin-bottom: 10px;
+  }
+  .item-handle__box {
+    margin-bottom: 15px;
+  }
+ }
+}
+</style>

+ 71 - 0
src/pages/category/components/Left.vue

@@ -0,0 +1,71 @@
+<template>
+  <div class="left-box">
+    <div class="switch-box">
+      <a-flex gap="10px" align="center" justify="center">
+        <span>列表</span>
+        <a-switch :checked="checked" @change="value => emits('change-switch', value)" />
+        <span>卡片</span>
+      </a-flex>
+    </div>
+    <a-list item-layout="horizontal" :data-source="data">
+      <template #renderItem="{ item }">
+        <a-list-item :class="activeId === item.categoryId ? 'row-actived' : ''">
+          <a-list-item-meta>
+            <template #description>
+              <div class="title" @click="emits('click-category', item)">{{ item.name }}</div>
+            </template>
+          </a-list-item-meta>
+        </a-list-item>
+      </template>
+    </a-list>
+  </div>
+</template>
+
+<script setup lang="js">
+const props = defineProps({
+  data: {
+    type: Array,
+    default: () => []
+  },
+  activeId: {
+    type: [String, Number],
+    default: ''
+  },
+  checked: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const emits = defineEmits(['click-category', 'change-switch']);
+
+</script>
+
+<style lang="less" scoped>
+.left-box {
+  .switch-box {
+    padding: 10px;
+    margin-bottom: 24px;
+    border: 1px solid @border-color;
+    border-radius: 2px;
+    span {
+      margin: 0 10px;
+    }
+  }
+  .title {
+    color: #000;
+    font-size: 14px;
+    cursor: pointer;
+  }
+
+  .row-actived {
+    color: #fff;
+    font-weight: 600;
+    background: @theme-color;
+    .title {
+      color: #fff;
+    }
+  }
+}
+
+</style>

+ 166 - 0
src/pages/category/components/Right.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="right-box">
+    <a-table
+      v-if="isList"
+      bordered
+      :dataSource="data"
+      :columns="mainData.columns"
+      :showHeader="false"
+      :pagination="false"
+    >
+      <template #emptyText>暂无商品信息</template>
+      <template #bodyCell="{ column, record }">
+        <template v-if="column.key === 'goodsName'">
+          <div class="goods-name" @click="viewDetail(record)">
+            <a-flex align="center">
+              <img :src="record.imgUrl" width="60" height="60" style="margin-right: 10px;" />
+              <span>{{ record.goodsName }}</span>
+            </a-flex>
+            <a-flex v-if="foramtTagData(record).length > 0" gutter="small" style="margin-top: 10px; margin-left: 70px;">
+              <a-tag
+                v-for="(tag, tIndex) in foramtTagData(record)"
+                :key="tIndex"
+                color="red"
+              >{{ tag }}</a-tag>
+            </a-flex>
+          </div>
+        </template>
+        <template v-if="column.key === 'soldNum'">
+          <div v-if="isLogin">销量: {{ record.soldNum }}</div>
+          <div v-else>
+            <a-button type="link" @click="toLogin">登录</a-button>
+            <span>查看销量</span>
+          </div>
+        </template>
+        <template v-if="column.key === 'goodsPrice'">
+          <div v-if="isLogin">价格: ¥ {{ record.goodsPrice }}</div>
+          <div v-else>
+            <a-button type="link" @click="toLogin">登录</a-button>
+            <span>查看价格</span>
+          </div>
+        </template>
+        <template v-if="column.key === 'opetation'">
+          <a-flex align="center" justify="space-between">
+            <a-button :disabled="!isLogin" type="primary" @click="handleAddToCart(record)">添加到购物车</a-button>
+            <a-button :disabled="!isLogin" type="primary">添加到列表</a-button>
+          </a-flex>
+        </template>
+      </template>
+    </a-table>
+    <div v-else class="grid-list__box">
+      <GridItem
+        v-for="item in data"
+        :key="item.goodsId"
+        :isLogin="isLogin"
+        :item="item"
+        @goods-detail="viewDetail(item)"
+        @to-login="toLogin"
+      />
+    </div>
+    <div style="margin-top: 10px;">
+      <a-pagination
+        show-size-changer
+        :current="pagination.pageNum"
+        :pageSize="pagination.pageSize"
+        :total="pagination.total"
+        :page-size-options="['6', '12', '24', '26', '48']"
+        @change="changePagination"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import { reactive, computed } from 'vue';
+import GridItem from './GridItem.vue';
+import { useUserStore } from '@/store/user';
+
+const props = defineProps({
+  data: {
+    type: Array,
+    default: () => []
+  },
+  pagination: {
+    type: Object,
+    default: () => ({
+      pageNum: 1,
+      pageSize: 10,
+      total: 0
+    })
+  },
+  isList: {
+    type: Boolean,
+    default: true
+  }
+})
+
+const emits = defineEmits(['change-pagination', 'goods-detail', 'add-to-cart', 'to-login']);
+
+const userStore = useUserStore();
+
+const isLogin = computed(() => userStore.isLogin);
+
+const mainData = reactive({
+  columns: [
+    {
+      title: '商品名称',
+      dataIndex: 'goodsName',
+      key: 'goodsName'
+    },
+    {
+      title: '商品库存',
+      dataIndex: 'soldNum',
+      key: 'soldNum',
+      width: 160
+    },
+    {
+      title: '商品价格',
+      dataIndex: 'goodsPrice',
+      key: 'goodsPrice',
+      width: 160
+    },
+    {
+      title: '操作',
+      dataIndex: 'opetation',
+      key: 'opetation',
+      width: 260
+    }
+  ]
+})
+
+const changePagination = (pageNum, pageSize) => {
+  emits('change-pagination', {pageNum, pageSize});
+}
+
+const viewDetail = (record) => {
+  emits('goods-detail', record);
+}
+
+const handleAddToCart = record => {
+  emits('add-to-cart', record)
+}
+
+const toLogin = () => {
+  emits('to-login')
+}
+
+const foramtTagData = record => {
+  const tags1 = Array.isArray(record.tags1) ? record.tags1 : [];
+  const tags2 = Array.isArray(record.tags2) ? record.tags2 : [];
+  return [].concat(tags1, tags2);
+}
+</script>
+
+<style lang="less" scoped>
+.goods-name {
+  color: @theme-color;
+  cursor: pointer;
+}
+
+.grid-list__box {
+  width: 100%;
+  display: grid;
+  gap: 16px;
+  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+}
+</style>

+ 134 - 0
src/pages/category/index.vue

@@ -0,0 +1,134 @@
+<template>
+  <div class="category-box">
+    <div class="category-title">{{ activedName }}</div>
+    <div class="categroy-content">
+      <div class="left">
+        <Left
+          :checked="mainData.isList"
+          :data="leftData"
+          :active-id="queryData.categoryId"
+          @change-switch="value => mainData.isList = value"
+          @click-category="changeChildCategoryFn"
+        />
+      </div>
+      <div class="right">
+        <Right
+          :data="rightData"
+          :pagination="{
+            pageNum: queryData.pageNum,
+            pageSize: queryData.pageSize,
+            total: queryData.total
+          }"
+          :is-list="mainData.isList"
+          @change-pagination="changePaginationFn"
+          @goods-detail="viewDetailFn"
+          @add-to-cart="addToCartFn"
+          @to-login="toLoginFn"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+<script setup lang="js">
+import Left from './components/Left.vue';
+import Right from './components/Right.vue';
+import { reactive, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { useHomeStore } from '@/store/home';
+import { useGoodsStore } from '@/store/goods';
+import { useCategoryStore } from '@/store/category';
+
+const router = useRouter();
+const homeStore = useHomeStore();
+const goodsStore = useGoodsStore();
+const categoryStore = useCategoryStore();
+
+const mainData = reactive({
+  isList: true
+})
+
+const tabIndex = computed(() => homeStore.tabIndex);
+const storageId = computed(() => homeStore.storageId);
+const categoryList = computed(() => categoryStore.list);
+const activedName = computed(() => categoryList.value[tabIndex.value]?.name || '');
+const leftData = computed(() => categoryList.value[tabIndex.value]?.children || []);
+const rightData = computed(() => goodsStore.list);
+const queryData = computed(() => goodsStore.params);
+
+const changeChildCategoryFn = item => {
+  if (item.categoryId == queryData.value.categoryId) return;
+  goodsStore.updateParams({
+    pageNum: 1,
+    total: 0,
+    categoryId: item.categoryId
+  });
+  goodsStore.resetListData();
+  goodsStore.fetchListData();
+}
+
+const changePaginationFn = (pagination) => {
+  goodsStore.updateParams({
+    pageNum: pagination.pageNum,
+    pageSize: pagination.pageSize
+  });
+  goodsStore.fetchListData();
+}
+
+const viewDetailFn = record => {
+  router.push({
+    path: '/goods',
+    query: {
+      id: record.goodsId
+    }
+  })
+}
+
+// 添加到购物车
+const addToCartFn = row => {
+  const buyGoods = [
+    {
+      goodsId: row.goodsId,
+      goodsSpecId: row.goodsSpecs ? row.goodsSpecs[0]?.goodsSpecId : '',
+      secKillId: row.secKillId || '',
+      promotionGroupId: row.promotionGroupId || ''
+    }
+  ]
+  const params = {
+    storageId: storageId.value,
+    buyGoods: buyGoods
+  }
+  goodsStore.addToCard(params)
+}
+
+const toLoginFn = () => {
+  router.push({
+    path: '/login'
+  })
+}
+
+</script>
+
+<style lang="less" scoped>
+.category-box {
+  padding: 20px;
+  background: #fff;
+  .category-title {
+    color: #000;
+    font-size: 28px;
+    margin-bottom: 24px;
+  }
+  .categroy-content {
+    width: 100%;
+    display: flex;
+    .left {
+      width: 220px;
+      flex-shrink: 0;
+      margin-right: 20px;
+    }
+    .right {
+      flex: 1;
+      min-width: 1px;
+    }
+ }
+}
+</style>

+ 280 - 0
src/pages/goods/index.vue

@@ -0,0 +1,280 @@
+<template>
+  <div class="detail-box">
+    <div class="main-info__box">
+      <div class="main-info__content">
+        <div class="goods-info">
+          <div class="image-box">
+            <img :src="mainForm.detail.imgUrl" />
+          </div>
+          <div class="info-content">
+            <div class="title">{{ mainForm.detail.goodsName || '' }}</div>
+            <div class="stock-info__box">
+              <div class="stock-content">
+                <a-flex v-if="!isLogin" align="center">
+                  <a-button type="link" @click="toLogin">登录</a-button>
+                  <span>查看商品实时库存</span>
+                </a-flex>
+                <div v-else>库存: {{ mainForm.detail.stock }}</div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="goods-handle">
+          <div>
+            <a-flex v-if="!isLogin" align="center">
+              <a-button type="link" @click="toLogin">登录</a-button>
+              <span>查看商品实时价格</span>
+            </a-flex>
+            <div v-else>价格: ¥ {{ mainForm.detail.goodsPrice }}</div>
+          </div>
+          <div>
+            <a-form
+              :model="mainForm"
+              name="mainForm"
+              :label-col="{ span: 24 }"
+              :wrapper-col="{ span: 24 }"
+              autocomplete="off"
+              @finish="onFinish"
+              @finishFailed="onFailed"
+            >
+              <a-form-item
+                label="数量"
+                name="quantity"
+                :rules="[{ required: true, message: '数量不能为空' }]"
+              >
+                <a-input-number v-model:value="mainForm.quantity" :min="1" style="width: 100%;">
+                  <template #upIcon>
+                    <ArrowUpOutlined />
+                  </template>
+                  <template #downIcon>
+                    <ArrowDownOutlined />
+                  </template>
+                </a-input-number>
+              </a-form-item>
+              <a-form-item label="" name="addToCard">
+                <a-button :disabled="!isLogin" type="primary" block @click="handleAddToCart">添加到购物车</a-button>
+              </a-form-item>
+              <a-form-item label="" name="saveToList">
+                <a-button :disabled="!isLogin" type="primary" block>保存到列表</a-button>
+              </a-form-item>
+            </a-form>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="main-desc__box">
+      <a-tabs v-model:activeKey="mainForm.tabIndex" type="card" :tabBarStyle="{background: '#f3f3f7', marginBottom: 0}">
+        <a-tab-pane
+          v-for="(tabName, index) in tabs"
+          :key="index"
+          :tab="tabName"
+        />
+      </a-tabs>
+      <div class="desc-content">
+        <div v-show="mainForm.tabIndex == 0">
+          <div v-html="mainForm.detail.content"></div>
+        </div>
+        <div v-show="mainForm.tabIndex == 1">
+          <div :style="{marginBottom: '20px'}">
+            <a-input
+              v-model:value="mainForm.searchFileName"
+              placeholder="点击输入文档名称搜索"
+              size="large"
+              allowClear
+            >
+              <template #prefix>
+                <SearchOutlined />
+              </template>
+            </a-input>
+          </div>
+          <a-collapse
+            v-for="(item, index) in goodsDocumentsRelaList"
+            :key="index"
+            collapsible="header"
+            :style="{marginBottom: '20px'}"
+          >
+            <a-collapse-panel key="1" :header="item.children && item.children.length > 0 ? `${item.folderName} (${item.children.length})` : item.parentCategoryName">
+              <div v-if="item.children && item.children.length > 0">
+                <ViewFile
+                  v-for="(child, cIndex) in item.children"
+                  :key="cIndex"
+                  :url="child.url"
+                  :name="child.fileName"
+                  :show-delete="false"
+                  :style="{
+                    marginBottom: cIndex === item.children.length - 1 ? 0 : '12px'
+                  }"
+                />
+              </div>
+            </a-collapse-panel>
+          </a-collapse>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import ViewFile from '@/components/viewFile/index.vue';
+import { ArrowUpOutlined, ArrowDownOutlined, SearchOutlined } from '@ant-design/icons-vue';
+import { reactive, computed, onMounted, watch } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { getDetail, getFileList } from '@/api/goods';
+import { useUserStore } from '@/store/user';
+import { useHomeStore } from '@/store/home';
+import { useGoodsStore } from '@/store/goods';
+import { goodsDetailData, fileList } from '@/utils/mock';
+
+const route = useRoute();
+const router = useRouter();
+const userStore = useUserStore();
+const homeStore = useHomeStore();
+const goodsStore = useGoodsStore();
+
+const mainForm = reactive({
+  quantity: 1,
+  tabIndex: 0,
+  detail: {},
+  searchFileName: ''
+})
+
+const isLogin = computed(() => userStore.isLogin);
+const goodsDocumentsRelaList = computed(() => {
+  if (!mainForm.detail.goodsDocumentsRelaList || mainForm.detail.goodsDocumentsRelaList.length === 0) return [];
+  const validList = mainForm.detail.goodsDocumentsRelaList.filter(item => item.goodsDocumentsId.indexOf(mainForm.searchFileName) > -1 || item.fileName.indexOf(mainForm.searchFileName) > -1);
+  return validList.reduce((prev, curItem) => {
+    const target = prev.find(item => item.goodsDocumentsId === curItem.goodsDocumentsId);
+    if (target) {
+      target.children.push(curItem);
+    } else {
+      prev.push({
+        ...curItem,
+        children: [curItem]
+      })
+    };
+    return prev;
+  }, []);
+});
+const tabs = computed(() => {
+  if (!mainForm.detail.goodsDocumentsRelaList || mainForm.detail.goodsDocumentsRelaList.length === 0) return ['商品详情'];
+  return ['商品详情', '文档'];
+});
+
+
+const onFinish = () => {}
+
+const onFailed = () => {}
+
+const fetchDetail = async () => {
+  // const res = await getDetail(route.query.id);
+  // mainForm.detail = res.data || {};
+  mainForm.detail = goodsDetailData;
+}
+
+const fetchFileData = async () => {
+  // const res = await getFileList(route.query.id);
+  // mainForm.detail.goodsDocumentsRelaList = res.data || [];
+  mainForm.detail.goodsDocumentsRelaList = fileList;
+}
+
+const handleAddToCart = () => {
+  if (!mainForm.detail.goodsId) return;
+  const buyGoods = [
+    {
+      goodsId: route.query.id,
+      goodsSpecId: mainForm.detail.goodsSpecs ? mainForm.detail.goodsSpecs[0]?.goodsSpecId : '',
+      secKillId: mainForm.detail.secKillId || '',
+      promotionGroupId: mainForm.detail.promotionGroupId || ''
+    }
+  ]
+  const params = {
+    storageId: homeStore.storageId,
+    buyGoods: buyGoods
+  }
+  goodsStore.addToCard(params)
+}
+
+const toLogin = () => {
+  router.push({
+    path: '/login'
+  })
+}
+
+onMounted(() => {
+  fetchDetail();
+  fetchFileData();
+})
+
+watch(() => route.query.id, newVal => {
+  fetchDetail();
+  fetchFileData();
+})
+
+</script>
+
+<style lang="less" scoped>
+.detail-box {
+  .main-info__box {
+    margin-bottom: 24px;
+    border: 1px solid @border-color;
+    border-radius: 2px;
+    .main-info__content {
+      width: 100%;
+      display: flex;
+      .goods-info {
+        flex: 1;
+        display: flex;
+        min-width: 1px;
+        background: #fff;
+        .image-box {
+          width: 360px;
+          height: 360px;
+          flex-shrink: 0;
+          img {
+            display: block;
+            width: 100%;
+            height: 100%;
+          }
+        }
+        .info-content {
+          flex: 1;
+          min-width: 1px;
+          padding: 20px;
+          word-break: break-all;
+          .title {
+            color: #000;
+            font-size: 24px;
+            font-weight: 600;
+            line-height: 1.5;
+            text-align: center;
+            margin-bottom: 24px;
+          }
+          .stock-info__box {
+            padding: 0 40px;
+            .stock-content {
+              padding: 20px 24px;
+              border: 1px solid @border-color;
+              border-radius: 2px;
+            }
+          }
+        }
+      }
+      .goods-handle {
+        width: 240px;
+        padding: 20px;
+        flex-shrink: 0;
+        background: #fff9db;
+      }
+    }
+  }
+  .main-desc__box {
+    .desc-content {
+      padding: 20px;
+      background: #fff;
+    }
+  }
+}
+::v-deep img {
+  max-width: 100% !important;
+}
+</style>

+ 78 - 0
src/pages/home/components/AccountModal.vue

@@ -0,0 +1,78 @@
+<template>
+  <a-drawer
+    :open="open"
+    title="账户"
+    placement="right"
+    @close="emits('close')"
+  >
+    <div class="drawer-box">
+      <div class="login-box">
+        <div class="title">您的账户</div>
+        <div class="desc">登录即可获取实时库存和准确的账户定价。通过保存的送达和取件设置, 结账更快。</div>
+        <div>
+          <a-button type="primary" size="large" @click="toLogin">登录</a-button>
+        </div>
+      </div>
+      <div class="reg-box">
+        <div class="title">创建账户</div>
+        <div class="desc">从我们丰富的顶级品牌空调设置、零件和耗材库存中注册购物账号。</div>
+        <div>
+          <a-button size="large" @click="toRegister">注册</a-button>
+        </div>
+      </div>
+    </div>
+  </a-drawer>
+</template>
+
+<script setup lang="js">
+import { useRouter } from 'vue-router';
+
+const router = useRouter()
+
+defineProps({
+  open: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const emits = defineEmits(['close']);
+
+const toLogin = () => {
+  router.push({
+    path: '/login'
+  })
+}
+
+const toRegister = () => {
+  router.push({
+    path: '/register'
+  })
+  emits('close')
+}
+</script>
+
+<style lang="less" scoped>
+.drawer-box {
+  .login-box,
+  .reg-box {
+    padding-bottom: 12px;
+    border-bottom: 1px solid @border-color;
+    .title {
+      margin-bottom: 20px;
+      font-size: 16px;
+      font-weight: 600;
+    }
+    .desc {
+      line-height: 1.5;
+      margin-bottom: 20px;
+    }
+  }
+  .reg-box {
+    border-bottom: none;
+    .title {
+      margin-top: 12px;
+    }
+  }
+}
+</style>

+ 42 - 0
src/pages/home/components/Category.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="classify-content">
+    <a-tabs
+      :activeKey="tabIndex"
+      :tabBarStyle="{color: '#fff', margin: 0}"
+      @change="changeTab"
+    >
+      <a-tab-pane
+        v-for="(item, index) in tabs"
+        :key="index"
+        :tab="item.name"
+      />
+    </a-tabs>
+  </div>
+</template>
+
+<script setup lang="js">
+const props = defineProps({
+  tabs: {
+    type: Array,
+    default: () => []
+  },
+  tabIndex: {
+    type: Number,
+    default: 0
+  }
+})
+
+const emits = defineEmits(['change'])
+
+const changeTab = (index) => {
+  emits('change', index)
+}
+
+</script>
+
+<style lang="less" scoped>
+.classify-content {
+  padding: 0 20px;
+  background: #003874;
+}
+</style>

+ 169 - 0
src/pages/home/components/HandleBar.vue

@@ -0,0 +1,169 @@
+<template>
+  <div class="handle-bar">
+    <a-flex gap="small" align="center" justify="space-between">
+      <div class="search-content">
+        <a-select
+          v-model:value="mainData.value"
+          style="width: 340px"
+          placeholder="点击输入商品名称搜索"
+          :filter-option="false"
+          :show-search="true"
+          :not-found-content="mainData.fetching ? undefined : null"
+          :options="mainData.goods"
+          @search="fetchGoodsData"
+          @select="selectGoodsFn"
+        >
+          <template v-if="mainData.fetching" #notFoundContent>
+            <a-spin size="small" />
+          </template>
+        </a-select>
+      </div>
+      <div class="handle-content">
+        <a-flex gap="large" align="center" justify="flex-end">
+          <a-dropdown placement="bottom">
+            <div class="handle-content__item">
+              <EnvironmentOutlined />
+              <span class="text">仓库切换</span>
+            </div>
+            <template #overlay>
+              <a-menu>
+                <a-menu-item
+                  v-for="(item, index) in storeList"
+                  :key="index"
+                >
+                  <div :class="item.storageId === storageId ? 'actived-store' : ''" @click="selectStore(item)">
+                    <a-flex align="center" justify="space-between">
+                      <div>{{ item.storageName }}</div>
+                      <CheckOutlined v-if="item.storageId === storageId" :style="{marginLeft: '10px'}" />
+                    </a-flex>
+                  </div>
+                </a-menu-item>
+              </a-menu>
+            </template>
+          </a-dropdown>
+          <div class="handle-content__item" @click="handleAccount">
+            <UserOutlined />
+            <span class="text">账户</span>
+          </div>
+          <div class="handle-content__item">
+            <CheckSquareOutlined />
+            <span class="text">列表</span>
+          </div>
+          <div class="handle-content__item">
+            <ShoppingCartOutlined :style="{fontSize: '16px'}" />
+            <span class="text">购物车</span>
+          </div>
+        </a-flex>
+      </div>
+    </a-flex>
+    <AccountModal :open="mainData.open" @close="mainData.open = false" />
+  </div>
+</template>
+
+<script setup lang="js">
+import AccountModal from './AccountModal.vue';
+import {
+  EnvironmentOutlined,
+  UserOutlined,
+  CheckSquareOutlined,
+  ShoppingCartOutlined,
+  CheckOutlined
+} from '@ant-design/icons-vue';
+import { reactive, watch } from 'vue';
+import { useRouter } from 'vue-router';
+import { debounce } from 'lodash-es';
+import { useGoodsStore } from '@/store/goods';
+
+const props = defineProps({
+  // 仓库列表
+  storeList: {
+    type: Array,
+    default: () => []
+  },
+  // 当前选择中的仓库
+  storageId: {
+    type: String,
+    default: ''
+  },
+  isLogin: {
+    type: Boolean,
+    default: false
+  }
+})
+
+const emits = defineEmits(['change-store']);
+
+const router = useRouter();
+const goodsStore = useGoodsStore();
+
+const mainData = reactive({
+  goods: [],
+  fetching: false,
+  showStore: false,
+  open: false
+})
+
+const fetchGoodsData = debounce(async (value) => {
+  if (value == '') return;
+  mainData.fetching = true;
+  const goodsData  = await goodsStore.fetchAllData({
+    pageNum: 1,
+    pageSize: -1,
+    keyword: value,
+    storageId: props.storageId
+  })
+  mainData.fetching = false;
+  mainData.goods = goodsData.map(item => ({
+    ...item,
+    label: item.goodsName,
+    value: item.goodsId
+  }))
+})
+
+const selectGoodsFn = goodsId => {
+  router.push({
+    path: '/goods',
+    query: {
+      id: goodsId
+    }
+  })
+}
+
+// 选择仓库
+const selectStore = row => {
+  if (props.storageId === row.storageId) return;
+  emits('change-store', row);
+}
+
+const handleAccount = () => {
+  if (props.isLogin) return;
+  mainData.open = true;
+}
+
+watch(() => mainData.value, (newVal) => {
+  if (!newVal) {
+    mainData.goods = [];
+    mainData.fetching = false;
+  }
+});
+
+</script>
+
+<style lang="less" scoped>
+.handle-bar {
+  padding: 0 20px;
+  background: @theme-color;
+  .handle-content__item {
+    padding: 20px 0;
+    color: #fff;
+    cursor: pointer;
+    .text {
+      margin-left: 6px;
+    }
+  }
+}
+
+.actived-store {
+  color: @theme-color !important;
+}
+</style>

+ 17 - 0
src/pages/home/components/Notice.vue

@@ -0,0 +1,17 @@
+<template>
+  <div class="notice-box">
+    <div class="notice-content">测试顶部通知栏</div>
+  </div>
+</template>
+
+<style lang="less" scoped>
+.notice-box {
+  padding: 14px 20px;
+  background: @theme-color;
+  .notice-content {
+    color: #fff;
+    font-size: 16px;
+    text-align: center;
+  }
+}
+</style>

+ 51 - 0
src/pages/home/components/Toolbar.vue

@@ -0,0 +1,51 @@
+<template>
+  <div class="headeer-toolbar">
+    <a-flex align="center" justify="flex-end">
+      <div class="toolbar-item">分支</div>
+      <div class="toolbar-item">培训</div>
+      <div class="toolbar-item">联系方式</div>
+      <a-dropdown placement="bottom">
+        <div class="toolbar-item">语言切换</div>
+        <template #overlay>
+          <a-menu>
+            <a-menu-item>
+              <div @click="handleLanguage('zh')">中文</div>
+            </a-menu-item>
+            <a-menu-item>
+              <div @click="handleLanguage('en')">英文</div>
+            </a-menu-item>
+            <a-menu-item>
+              <div @click="handleLanguage('es')">西班牙</div>
+            </a-menu-item>
+          </a-menu>
+        </template>
+      </a-dropdown>
+    </a-flex>
+  </div>
+</template>
+
+<script setup lang="js">
+const handleLanguage = language => {
+  window.Vue_Translation_Of_Text_Type = language
+  window.localStorage.setItem('Vue_Translation_Of_Text_Type', language)
+  // 重新加载页面
+  window.location.reload()
+  setTimeout(() => {
+    window.location.reload();
+  }, 250)
+}
+</script>
+
+<style lang="less" scoped>
+.headeer-toolbar {
+  background: #fff;
+  .toolbar-item {
+    color: #000;
+    padding: 12px 20px;
+    cursor: pointer;
+    &:hover {
+      color: @theme-color;
+    }
+  }
+}
+</style>

+ 113 - 0
src/pages/home/index.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="home-page">
+    <header>
+      <Notice />
+      <Toolbar />
+      <HandleBar
+        :isLogin="isLogin"
+        :storageId="storageId"
+        :storeList="mainData.storeList"
+        @change-store="changeStoreFn"
+      />
+      <Category :tabs="categoryList" :tab-index="tabIndex" @change="changeTbaFn" />
+    </header>
+    <main class="app-main__content">
+      <RouterView />
+    </main>
+    <div v-if="showBackIcon" class="back-icon__box">
+      <a-button type="primary" shape="round" @click="router.back()">back</a-button>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import Notice from './components/Notice.vue';
+import Toolbar from './components/Toolbar.vue';
+import HandleBar from './components/HandleBar.vue';
+import Category from './components/Category.vue';
+import { onMounted, computed, reactive } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { useUserStore } from '@/store/user';
+import { useHomeStore } from '@/store/home';
+import { useGoodsStore } from '@/store/goods';
+import { useCategoryStore } from '@/store/category';
+import { getStoreList, getNotice, transformLanguageText } from '@/api/common';
+import { storeList } from '@/utils/mock';
+
+const route = useRoute();
+const router = useRouter();
+const userStore = useUserStore();
+const homeStore = useHomeStore();
+const goodsStore = useGoodsStore();
+const categoryStore = useCategoryStore();
+
+const tabIndex = computed(() => homeStore.tabIndex);
+const storageId = computed(() => homeStore.storageId);
+const categoryList = computed(() => categoryStore.list);
+const isLogin = computed(() => userStore.isLogin);
+const showBackIcon = computed(() => route.path !== '/category');
+
+const mainData = reactive({
+  storeList: []
+})
+
+const resetGoodsDataAndFetch = () => {
+  goodsStore.resetParams();
+  goodsStore.resetListData();
+  goodsStore.fetchListData();
+}
+
+const changeTbaFn = newTabIndex => {
+  if (newTabIndex == tabIndex.value) return;
+  homeStore.changeTab(newTabIndex);
+  if (route.path !== '/category') {
+    router.push({
+      path: '/category'
+    })
+  } else {
+    resetGoodsDataAndFetch();
+  }
+}
+
+const fetchStoreListData = async () => {
+  // const res = await getStoreList();
+  // mainData.storeList = res.data || [];
+  mainData.storeList = storeList;
+  if (mainData.storeList.length > 0) {
+    homeStore.updateStorageId(mainData.storeList[0].storageId);
+  }
+}
+
+const changeStoreFn = row => {
+  homeStore.updateStorageId(row.storageId);
+  homeStore.resetTab();
+  categoryStore.fetchListData();
+  resetGoodsDataAndFetch();
+}
+
+const fetchNoticeData = async () => {
+  const res = await getNotice();
+  console.log(res)
+}
+
+onMounted(() => {
+  categoryStore.fetchListData();
+  resetGoodsDataAndFetch();
+  fetchStoreListData();
+  // fetchNoticeData();
+})
+
+</script>
+
+<style lang="less" scoped>
+.app-main__content {
+  padding: 20px;
+}
+
+.back-icon__box {
+  position: fixed;
+  bottom: 30px;
+  right: 30px;
+  z-index: 9999;
+}
+</style>

+ 157 - 0
src/pages/login/index.vue

@@ -0,0 +1,157 @@
+<template>
+  <div class="login-box">
+    <div class="form-content">
+      <div class="logo-box">
+        <Logo />
+      </div>
+      <a-form
+        ref="formRef"
+        name="form"
+        :model="mainForm"
+        :label-col="{ style: {width: '100px'}}"
+        :wrapper-col="{ span: 16 }"
+        autocomplete="off"
+        @finish="onFinish"
+      >
+        <a-form-item
+          label="账号"
+          name="account"
+          :rules="[{ required: true, message: '账号不能为空' }]"
+        >
+          <a-input
+            v-model:value="mainForm.account"
+            placeholder="请输入账号"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="密码"
+          name="password"
+          :rules="[{ required: true, message: '密码不能为空' }]"
+        >
+          <a-input-password
+            v-model:value="mainForm.password"
+            placeholder="请输入密码"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="验证码"
+          name="codeValue"
+          :rules="[
+            { required: true, message: '验证码不能为空' }
+          ]"
+        >
+          <a-input-group compact>
+            <a-input
+              v-model:value="mainForm.codeValue"
+              placeholder="请输入验证码"
+              allowClear
+              style="width: calc(100% - 128px)"
+            />
+            <img :src="mainForm.pic" @click="reFetchCode" :style="{width: '128px', height: '32px', cursor: 'pointer'}" />
+          </a-input-group>
+        </a-form-item>
+        <a-form-item label=" " :colon="false">
+          <a-button
+            type="primary"
+            html-type="submit"
+            size="large"
+            block
+            :disabled="mainForm.disabled"
+          >登 录</a-button>
+        </a-form-item>
+      </a-form>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import Logo from '@/components/logo/index.vue';
+import { ref, reactive, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { message } from 'ant-design-vue';
+import { getCode, userLogin } from '@/api/user';
+import { useUserStore } from '@/store/user';
+import { setToken } from '@/utils/token';
+
+const router = useRouter();
+const userStore = useUserStore();
+const formRef = ref(null)
+const mainForm = reactive({
+  account: '',
+  password: '',
+  code: '',
+  pic: '',
+  codeValue: '',
+  disabled: false
+})
+
+const fetchCode = async () => {
+  mainForm.disabled = true
+  getCode().then(res => {
+    mainForm.code = res.data?.code || '';
+    mainForm.pic = 'data:image/jpeg;base64,' + res.data?.pic;
+  }).finally(() => {
+    mainForm.disabled = false
+  })
+}
+
+const fetchLogin = async () => {
+  mainForm.disabled = true
+  const params = {
+    userName: mainForm.account,
+    password: mainForm.password,
+    code: mainForm.code,
+    codeValue: mainForm.codeValue,
+    clientType: '1'
+  }
+  userLogin(params).then(res => {
+    setToken(res.data.token);
+    message.success('登录成功');
+    userStore.updateUserInfo(res.data);
+    router.back();
+  }).catch(() => {
+    fetchCode();
+  }).finally(() => {
+    mainForm.disabled = false
+  })
+}
+
+const onFinish = () => {
+  fetchLogin();
+}
+
+const reFetchCode = () => {
+  if (mainForm.disabled) return;
+  fetchCode();
+}
+
+onMounted(() => {
+  fetchCode()
+})
+
+</script>
+
+<style lang="less" scoped>
+.login-box {
+  width: 100vw;
+  height: 100vh;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #f2f2f2;
+  .form-content {
+    width: 520px;
+    padding: 24px;
+    background: #fff;
+    border-radius: 10px;
+    .logo-box {
+      width: 100px;
+      height: 100px;
+      margin: 0 auto;
+      margin-bottom: 20px;
+    }
+  }
+}
+</style>

+ 148 - 0
src/pages/register/index.vue

@@ -0,0 +1,148 @@
+<template>
+  <div class="register-box">
+    <div class="form-content">
+      <div class="logo-box">
+        <Logo />
+      </div>
+      <a-form
+        ref="formRef"
+        name="form"
+        :model="mainForm"
+        :label-col="{ style: {width: '100px'}}"
+        :wrapper-col="{ span: 16 }"
+        autocomplete="off"
+        @finish="onFinish"
+      >
+        <a-form-item
+          label="名字"
+          name="firstName"
+          :rules="[{ required: true, message: '名字不能为空' }]"
+        >
+          <a-input
+            v-model:value="mainForm.firstName"
+            placeholder="请输入名字"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="姓"
+          name="lastName"
+          :rules="[{ required: true, message: '姓不能为空' }]"
+        >
+          <a-input
+            v-model:value="mainForm.lastName"
+            placeholder="请输入姓"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="邮箱"
+          name="email"
+          :rules="[
+            { required: true, validator: checkEmail },
+          ]"
+        >
+          <a-input
+            v-model:value="mainForm.email"
+            placeholder="请输入邮箱"
+            allowClear
+          />
+          <div>您的凭证将发送到此邮箱。</div>
+        </a-form-item>
+        <a-form-item
+          label="公司名称"
+          name="companyName"
+          :rules="[{ required: true, message: '公司名称不能为空' }]"
+        >
+          <a-input
+            v-model:value="mainForm.companyName"
+            placeholder="请输入公司名称"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="手机号码"
+          name="mobile"
+          :rules="[
+            { required: true, validator: checkMobile }
+          ]"
+        >
+          <a-input
+            v-model:value="mainForm.mobile"
+            placeholder="请输入手机号码"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item
+          label="备注"
+          name="message"
+        >
+          <a-input
+            v-model:value="mainForm.message"
+            placeholder="请输入备注"
+            allowClear
+          />
+        </a-form-item>
+        <a-form-item label=" " :colon="false">
+          <a-button type="primary" html-type="submit" size="large" block>提 交</a-button>
+        </a-form-item>
+      </a-form>
+    </div>
+  </div>
+</template>
+
+<script setup lang="js">
+import { ref, reactive } from 'vue';
+import Logo from '@/components/logo/index.vue'
+
+const formRef = ref(null)
+const mainForm = reactive({
+  firstName: '',
+  lastName: '',
+  email: '',
+  companyName: '',
+  mobile: '',
+  message: ''
+})
+
+const onFinish = () => {
+  console.log('--- finish ---')
+}
+
+const checkEmail = (rule, value) => {
+  if (value === '') return Promise.reject('邮箱不能为空');
+  const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
+  if (!regex.test(value)) return Promise.reject('邮箱格式有误');
+  return Promise.resolve()
+}
+
+const checkMobile = (rule, value) => {
+  if (value === '') return Promise.reject('手机号码不能为空');
+  const internationalPhoneRegex = /^(\+?0?86\-?)?1[3-9]\d{9}$|^\+[1-9]\d{0,3}[1-9]\d{5,14}$/
+  if (!internationalPhoneRegex.test(value)) return Promise.reject('手机号码格式有误');
+  return Promise.resolve()
+}
+</script>
+
+<style lang="less" scoped>
+.register-box {
+  width: 100vw;
+  height: 100vh;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: #f2f2f2;
+  .form-content {
+    width: 520px;
+    padding: 24px;
+    background: #fff;
+    border-radius: 10px;
+    .logo-box {
+      width: 100px;
+      height: 100px;
+      margin: 0 auto;
+      margin-bottom: 20px;
+    }
+  }
+}
+</style>

+ 139 - 0
src/reset.css

@@ -0,0 +1,139 @@
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+  margin: 0;
+  padding: 0;
+  border: 0;
+  font-size: 100%;
+  font: inherit;
+}
+
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section {
+  display: block;
+}
+
+/* HTML5 hidden-attribute fix for newer browsers */
+*[hidden] {
+  display: none;
+}
+
+html,
+body {
+  min-height: 100vh;
+  line-height: 1;
+}
+
+menu,
+ol,
+ul {
+  list-style: none;
+}
+
+blockquote,
+q {
+  quotes: none;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+  content: '';
+  content: none;
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}

+ 57 - 0
src/router/index.js

@@ -0,0 +1,57 @@
+import { createRouter, createWebHistory } from 'vue-router'
+import Home from '@/pages/home/index.vue'
+import Category from '@/pages/category/index.vue'
+import Login from '@/pages/login/index.vue'
+import Register from '@/pages/register/index.vue'
+
+const routes = [
+  {
+    path: '/',
+    redirect: '/category',
+    component: Home,
+    meta: {
+      title: '首页'
+    },
+    children: [
+      {
+        path: '/category',
+        component: Category,
+        meta: {
+          title: '首页'
+        }
+      },
+      {
+        path: '/goods',
+        component: () => import('@/pages/goods/index.vue'),
+        meta: {
+          title: '商品详情'
+        }
+      }
+    ]
+  },
+  {
+    path: '/login',
+    component: Login,
+    meta: {
+      title: '登录'
+    }
+  },
+  {
+    path: '/register',
+    component: Register,
+    meta: {
+      title: '注册'
+    }
+  }
+]
+
+const router = createRouter({
+  history: createWebHistory(),
+  routes
+})
+
+router.afterEach((to, before) => {
+  document.title = to.meta?.title || '';
+})
+
+export default router

+ 16 - 0
src/store/category.js

@@ -0,0 +1,16 @@
+import { defineStore } from 'pinia';
+import { getList } from '@/api/category';
+import { categoryList } from '@/utils/mock';
+
+export const useCategoryStore = defineStore('category', {
+  state: () => ({
+    list: []
+  }),
+  actions: {
+    async fetchListData() {
+      // const res = await getList();
+      // this.list = res.data || [];
+      this.list = categoryList;
+    }
+  }
+})

+ 53 - 0
src/store/goods.js

@@ -0,0 +1,53 @@
+import { defineStore } from 'pinia';
+import { message } from 'ant-design-vue';
+import { getList, addToCard } from '@/api/goods';
+import { goodsList } from '@/utils/mock';
+
+export const useGoodsStore = defineStore('goods', {
+  state: () => ({
+    params: {
+      pageNum: 1,
+      pageSize: 24,
+      total: 0,
+      categoryId: ''
+    },
+    list: []
+  }),
+  actions: {
+    resetListData() {
+      this.list = [];
+    },
+    async fetchListData() {
+      // const res = await getList(this.params);
+      // this.list = res.data?.record || [];
+      // this.params.total = res.data?.total || 0;
+      this.list = goodsList;
+      this.params.total = 120;
+    },
+    async fetchAllData(params) {
+      // const res = await getList(params);
+      // return Promise.resolve(res.data || []);
+      return new Promise(resolve => resolve(goodsList))
+    },
+    resetParams() {
+      this.params.pageNum = 1;
+      this.params.total = 0;
+      this.params.categoryId = '';
+    },
+    updateParams(object = {}) {
+      Object.keys(object).forEach(key => {
+        if (this.params[key] != undefined) {
+          this.params[key] = object[key];
+        }
+      })
+    },
+    addToCard(params, success, fail) {
+      addToCard(params).then(() => {
+        message.success('添加成功')
+        success && success()
+      }).catch(() => {
+        fail && fail()
+      })
+    }
+  }
+})

+ 19 - 0
src/store/home.js

@@ -0,0 +1,19 @@
+import { defineStore } from 'pinia'
+
+export const useHomeStore = defineStore('home', {
+  state: () => ({
+    tabIndex: 0,
+    storageId: ''
+  }),
+  actions: {
+    changeTab(tabIndex) {
+      this.tabIndex = tabIndex
+    },
+    resetTab() {
+      this.tabIndex = 0;
+    },
+    updateStorageId(storageId) {
+      this.storageId = storageId
+    }
+  }
+})

+ 4 - 0
src/store/index.js

@@ -0,0 +1,4 @@
+import { createPinia } from 'pinia';
+
+const pinia = createPinia();
+export default pinia;

+ 18 - 0
src/store/user.js

@@ -0,0 +1,18 @@
+import { defineStore } from 'pinia';
+
+export const useUserStore = defineStore('user', {
+  state: () => ({
+    userInfo: null
+  }),
+  actions: {
+    updateUserInfo(userInfo) {
+      this.userInfo = userInfo
+    },
+    clearUserInfo() {
+      this.userInfo = null
+    }
+  },
+  getters: {
+    isLogin: state => state.userInfo !== null 
+  }
+})

+ 21 - 0
src/styles/common.less

@@ -0,0 +1,21 @@
+// .ant-modal-body {
+//   position: relative !important;
+//   &::before {
+//     content: " " !important;
+//     position: absolute !important;
+//     top: 0 !important;
+//     left: 0 !important;
+//     width: calc(100% + 24px) !important;
+//     height: 1px !important;
+//     background: @border-color !important;
+//   }
+//   &::after {
+//     content: " " !important;
+//     position: absolute !important;
+//     bottom: 0 !important;
+//     left: 0 !important;
+//     width: calc(100% + 24px) !important;
+//     height: 1px !important;
+//     background: @border-color !important;
+//   }
+// }

+ 4 - 0
src/styles/variables.less

@@ -0,0 +1,4 @@
+// 主题色彩系统
+// @theme-color: #1677ff;
+@theme-color: #045ea9;
+@border-color: #D0D0DE;

+ 38 - 0
src/utils/antd-locale-config.js

@@ -0,0 +1,38 @@
+// antd-locale-config.js
+// 或者使用默认导入(推荐)
+import zh_CN from 'ant-design-vue/es/locale/zh_CN'
+import en_US from 'ant-design-vue/es/locale/en_US'
+import es_ES from 'ant-design-vue/es/locale/es_ES'
+
+// 语言包映射
+export const antdLocales = {
+  'zh': zh_CN,
+  'zh-CN': zh_CN,
+  'en': en_US,
+  'en-US': en_US,
+  'es': es_ES,
+  'es-ES': es_ES
+}
+
+// 配置 Ant Design 国际化
+export function setupAntdLocale(app, language) {
+  const localeKey = Object.keys(antdLocales).find(key => 
+    key.startsWith(language)
+  ) || 'zh-CN'
+  
+  const locale = antdLocales[localeKey]
+  
+  // 配置 ConfigProvider
+  app.provide('locale', locale)
+  
+  return locale
+}
+
+// 获取当前语言的 Ant Design 配置
+export function getAntdLocale(language) {
+  const localeKey = Object.keys(antdLocales).find(key => 
+    key.startsWith(language)
+  ) || 'zh-CN'
+  
+  return antdLocales[localeKey]
+}

+ 122 - 0
src/utils/antd-processor.js

@@ -0,0 +1,122 @@
+// antd-processor.js
+import { domProcessor } from './text-processor'
+
+// Ant Design Vue 特定处理器
+export const antdProcessor = {
+  // Ant Design 组件选择器
+  antdSelectors: [
+    '.ant-btn',                    // 按钮
+    '.ant-btn > span',             // 按钮文本
+    '.ant-modal-title',            // 模态框标题
+    '.ant-menu-item',              // 菜单项
+    '.ant-tabs-tab',               // 标签页
+    '.ant-table-title',            // 表格标题
+    '.ant-pagination-item',        // 分页项
+    '.ant-select-item',            // 选择器选项
+    '.ant-form-item-label > label', // 表单标签
+    '.ant-card-head-title',        // 卡片标题
+    '.ant-descriptions-item-label', // 描述列表标签
+    '.ant-descriptions-item-content', // 描述列表内容
+    '.ant-result-title',           // 结果页标题
+    '.ant-result-subtitle',        // 结果页副标题
+    '.ant-empty-description',      // 空状态描述
+    '.ant-alert-message',          // 警告框消息
+    '.ant-alert-description',      // 警告框描述
+    '.ant-table',
+    '.ant-table-cell'
+  ],
+
+  // 初始化 DOM 观察器
+  initObserver() {
+    if (this.observer) return
+
+    this.observer = new MutationObserver((mutations) => {
+      mutations.forEach((mutation) => {
+        mutation.addedNodes.forEach((node) => {
+          if (node.nodeType === Node.ELEMENT_NODE) {
+            this.processAntdComponent(node)
+            this.processDynamicContent(node)
+          }
+        })
+      })
+    })
+
+    this.observer.observe(document.body, {
+      childList: true,
+      subtree: true,
+      attributes: true,
+      attributeFilter: ['title', 'placeholder', 'aria-label']
+    })
+  },
+
+  // 处理所有 Ant Design 组件
+  processAntdComponents() {
+    this.antdSelectors.forEach(selector => {
+      try {
+        document.querySelectorAll(selector).forEach(element => {
+          this.processAntdComponent(element)
+        })
+      } catch (error) {
+        console.warn(`处理选择器 ${selector} 失败:`, error)
+      }
+    })
+
+    // 处理动态内容
+    this.processDynamicComponents()
+  },
+
+  // 处理单个 Ant Design 组件
+  processAntdComponent(element) {
+    if (!element || element._antdProcessed) return
+    element._antdProcessed = true
+
+    domProcessor.processElement(element)
+  },
+
+  // 处理动态生成的组件
+  processDynamicComponents() {
+    // 处理消息提示
+    this.processSelector('.ant-message .ant-message-notice-content')
+    
+    // 处理通知
+    this.processSelector('.ant-notification-notice')
+    
+    // 处理工具提示
+    this.processSelector('.ant-tooltip-inner')
+    
+    // 处理弹出框
+    this.processSelector('.ant-popover-inner')
+  },
+
+  // 处理选择器
+  processSelector(selector) {
+    document.querySelectorAll(selector).forEach(element => {
+      this.processAntdComponent(element)
+    })
+  },
+
+  // 处理动态内容
+  processDynamicContent(element) {
+    // 处理消息提示
+    if (element.classList?.contains('ant-message')) {
+      element.querySelectorAll('.ant-message-notice-content').forEach(notice => {
+        this.processAntdComponent(notice)
+      })
+    }
+
+    // 处理通知
+    if (element.classList?.contains('ant-notification')) {
+      element.querySelectorAll('.ant-notification-notice').forEach(notice => {
+        this.processAntdComponent(notice)
+      })
+    }
+  },
+
+  // 销毁观察器
+  destroy() {
+    if (this.observer) {
+      this.observer.disconnect()
+      this.observer = null
+    }
+  }
+}

+ 69 - 0
src/utils/init-processor.js

@@ -0,0 +1,69 @@
+// init-processor.js
+import axios from 'axios'
+import { ConfigProvider } from 'ant-design-vue'
+import { GlobalTextProcessor  } from './vue3-plugin'
+const WindowsTranslateApi = 'https://jiasm.zfire.top/translate'
+
+// 初始化函数
+export const translaBeforeRegistration = async (callback, language = '') => {
+  window.Vue_Translation_Of_Text_Type = 
+    window.localStorage.getItem('Vue_Translation_Of_Text_Type') || language || 'zh'
+  
+  const supportedLanguages = ['en', 'es', 'zh'] // 支持中文、英文、西班牙语
+  
+  if (window.Vue_Translation_Of_Text_Type && 
+      supportedLanguages.includes(window.Vue_Translation_Of_Text_Type)) {
+    try {
+      const response = await axios.post(
+        `${WindowsTranslateApi}/api/v1/common/readTranslationOfText`,
+        {
+          targetLanguage: window.Vue_Translation_Of_Text_Type
+        }
+      )
+      
+      if (response.data?.data) {
+        const obj = {}
+        const obj2 = {}
+        response.data.data.forEach(item => {
+          obj[item.source] = item.target
+          obj2[item.target] = true
+        })
+        
+        window.Vue_Translation_Of_Text_Old_Data = { 
+          ...(window.Vue_Translation_Of_Text_Old_Data || {}), 
+          ...obj2 
+        }
+        window.Vue_Translation_Of_Text_Data = { 
+          ...(window.Vue_Translation_Of_Text_Data || {}), 
+          ...obj 
+        }
+      }
+    } catch (error) {
+      console.error('Initial translation load failed:', error)
+    }
+  }
+  
+  callback?.()
+}
+
+// Ant Design 国际化配置
+export function setupAntdLocale(app, language) {
+  // 动态导入 Ant Design 语言包
+  const antdLocales = {
+    'zh': () => import('ant-design-vue/es/locale/zh_CN').then(m => m.default),
+    'en': () => import('ant-design-vue/es/locale/en_US').then(m => m.default),
+    'es': () => import('ant-design-vue/es/locale/es_ES').then(m => m.default)
+  }
+
+  const localeLoader = antdLocales[language] || antdLocales['zh']
+  
+  localeLoader().then(locale => {
+    // 配置 Ant Design 国际化
+    app.use(ConfigProvider, {
+      locale,
+      componentSize: 'middle'
+    })
+  })
+}
+
+export {GlobalTextProcessor}

+ 778 - 0
src/utils/mock.js

@@ -0,0 +1,778 @@
+export const storeList = [
+  {
+    "storageId": "1955809643963543554",
+    "companyWechatId": "1831608878894858241",
+    "companyName": "广州众炬科技有限公司",
+    "storageName": "家盛茂广州仓",
+    "storageAddress": "广州",
+    "storageMobile": null,
+    "type": "商品",
+    "status": true,
+    "isDefault": true,
+    "createBy": null,
+    "createTime": null,
+    "updateBy": null,
+    "updateTime": null,
+    "lng": "113.26",
+    "lat": "23.13",
+    "del": null,
+    "distance": "7.58"
+  },
+  {
+    "storageId": "1962416520713388033",
+    "companyWechatId": "1831608878894858241",
+    "companyName": "广州众炬科技有限公司",
+    "storageName": "家盛茂佛山仓测试",
+    "storageAddress": "佛山",
+    "storageMobile": "15915843960",
+    "type": "商品",
+    "status": true,
+    "isDefault": false,
+    "createBy": null,
+    "createTime": null,
+    "updateBy": null,
+    "updateTime": null,
+    "lng": "113.12",
+    "lat": "23.02",
+    "del": null,
+    "distance": "21.89"
+  },
+  {
+    "storageId": "1976225161828552706",
+    "companyWechatId": "1976225161828552706",
+    "companyName": "商户1",
+    "storageName": "默认仓",
+    "storageAddress": "",
+    "storageMobile": null,
+    "type": "商品",
+    "status": true,
+    "isDefault": true,
+    "createBy": null,
+    "createTime": null,
+    "updateBy": null,
+    "updateTime": null,
+    "lng": "116.41",
+    "lat": "39.90",
+    "del": null,
+    "distance": "1891.66"
+  }
+]
+
+export const categoryList = [
+  {
+    "categoryId": "18316088788948582411807614685524709378",
+    "companyWechatId": "0",
+    "companyName": "",
+    "name": "空调",
+    "type": null,
+    "status": true,
+    "isServiceShow": true,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2024-07/171980389792010a3f414-3f77-419a-9c61-edee4f40bcd6.jpg",
+    "goodsNum": 0,
+    "level": 1,
+    "parentId": "1831608878894858241null",
+    "sortNum": 0,
+    "createTime": "2024-09-05 16:22:41",
+    "updateBy": "(admin)admin",
+    "updateTime": "2024-09-05 16:22:41",
+    "createBy": "(admin)admin",
+    "del": null,
+    "mainName": "",
+    "mainNumber": "",
+    "smallName": null,
+    "smallNumber": null,
+    "taxNumber": "",
+    "taxPercent": null,
+    "goodsNumber": null,
+    "workOrderType": null,
+    "demandTypeId": null,
+    "demandSmallTypeId": null,
+    "typeOption": null,
+    "typeAttribute": null,
+    "goodsCategoryStr": null,
+    "children": [
+      {
+        "categoryId": "18316088788948582411807614745054466050",
+        "companyWechatId": "0",
+        "companyName": "",
+        "name": "挂式空调",
+        "type": 1,
+        "status": true,
+        "isServiceShow": false,
+        "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2024-07/1719803912347f053f20c-bb7f-4144-9aaa-a8e06d9f8549.jpg",
+        "goodsNum": 0,
+        "level": 2,
+        "parentId": "18316088788948582411807614685524709378",
+        "sortNum": 0,
+        "createTime": "2024-09-05 16:22:41",
+        "updateBy": "(admin)admin",
+        "updateTime": "2024-09-05 16:22:41",
+        "createBy": "(admin)admin",
+        "del": false,
+        "mainName": "",
+        "mainNumber": "",
+        "smallName": null,
+        "smallNumber": null,
+        "taxNumber": "",
+        "taxPercent": null,
+        "goodsNumber": null,
+        "workOrderType": "INSTALL",
+        "demandTypeId": 0,
+        "demandSmallTypeId": 0,
+        "typeOption": "SIGIN",
+        "typeAttribute": null,
+        "goodsCategoryStr": null,
+        "children": null,
+        "goodsCategoryRelaList": null
+      },
+      {
+        "categoryId": "18316088788948582411813384470074126338",
+        "companyWechatId": "0",
+        "companyName": "",
+        "name": "柜式空调",
+        "type": 1,
+        "status": true,
+        "isServiceShow": false,
+        "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2024-07/172117952067127994678-647b-4a32-8a4a-9d4255661d12.jpg",
+        "goodsNum": 0,
+        "level": 2,
+        "parentId": "18316088788948582411807614685524709378",
+        "sortNum": 0,
+        "createTime": "2024-09-05 16:22:41",
+        "updateBy": "(admin)admin",
+        "updateTime": "2024-09-05 16:22:41",
+        "createBy": "(admin)admin",
+        "del": false,
+        "mainName": "",
+        "mainNumber": "",
+        "smallName": null,
+        "smallNumber": null,
+        "taxNumber": "",
+        "taxPercent": null,
+        "goodsNumber": null,
+        "workOrderType": "INSTALL",
+        "demandTypeId": 0,
+        "demandSmallTypeId": 0,
+        "typeOption": "SIGIN",
+        "typeAttribute": null,
+        "goodsCategoryStr": null,
+        "children": null,
+        "goodsCategoryRelaList": null
+      }
+    ],
+    "goodsCategoryRelaList": null
+  },
+  {
+    "categoryId": "18316088788948582411814485954580873217",
+    "companyWechatId": "0",
+    "companyName": "",
+    "name": "家用中央空调",
+    "type": null,
+    "status": true,
+    "isServiceShow": true,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2024-07/17214421362130c15c8b1-9441-427f-bbbf-dc0351989411.jpg",
+    "goodsNum": 0,
+    "level": 1,
+    "parentId": "1831608878894858241null",
+    "sortNum": 0,
+    "createTime": "2024-09-05 16:22:41",
+    "updateBy": "(admin)admin",
+    "updateTime": "2024-09-05 16:22:41",
+    "createBy": "(admin)admin",
+    "del": null,
+    "mainName": "",
+    "mainNumber": "",
+    "smallName": null,
+    "smallNumber": null,
+    "taxNumber": "",
+    "taxPercent": null,
+    "goodsNumber": null,
+    "workOrderType": null,
+    "demandTypeId": null,
+    "demandSmallTypeId": null,
+    "typeOption": null,
+    "typeAttribute": null,
+    "goodsCategoryStr": null,
+    "children": [
+      {
+        "categoryId": "1978689558056529922",
+        "companyWechatId": "0",
+        "companyName": "",
+        "name": "家装中央空调",
+        "type": 1,
+        "status": true,
+        "isServiceShow": false,
+        "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/17605913181675310b6f3-89a6-4b94-8457-21ba748058f1.jpg",
+        "goodsNum": 0,
+        "level": 2,
+        "parentId": "18316088788948582411814485954580873217",
+        "sortNum": 1,
+        "createTime": "2025-10-16 13:08:48",
+        "updateBy": "(ab1313131313)家盛茂测试",
+        "updateTime": "2025-10-16 13:08:48",
+        "createBy": "(ab1313131313)家盛茂测试",
+        "del": false,
+        "mainName": "",
+        "mainNumber": "",
+        "smallName": null,
+        "smallNumber": null,
+        "taxNumber": "",
+        "taxPercent": null,
+        "goodsNumber": null,
+        "workOrderType": "INSTALL",
+        "demandTypeId": 0,
+        "demandSmallTypeId": 0,
+        "typeOption": "SIGIN",
+        "typeAttribute": null,
+        "goodsCategoryStr": null,
+        "children": null,
+        "goodsCategoryRelaList": null
+      }
+    ],
+    "goodsCategoryRelaList": null
+  },
+  {
+    "categoryId": "1960991889347506178",
+    "companyWechatId": "0",
+    "companyName": "",
+    "name": "清洗保养",
+    "type": null,
+    "status": true,
+    "isServiceShow": true,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-08/175637185786609849167-902f-415b-b052-7db96a8fb984.jpg",
+    "goodsNum": 0,
+    "level": 1,
+    "parentId": null,
+    "sortNum": 0,
+    "createTime": "2025-08-28 17:04:35",
+    "updateBy": "(ab1313131313)家盛茂测试",
+    "updateTime": "2025-08-28 17:04:35",
+    "createBy": "(ab1313131313)家盛茂测试",
+    "del": null,
+    "mainName": "",
+    "mainNumber": "",
+    "smallName": null,
+    "smallNumber": null,
+    "taxNumber": "",
+    "taxPercent": null,
+    "goodsNumber": null,
+    "workOrderType": null,
+    "demandTypeId": null,
+    "demandSmallTypeId": null,
+    "typeOption": null,
+    "typeAttribute": null,
+    "goodsCategoryStr": null,
+    "children": [
+      {
+        "categoryId": "1960992090288222209",
+        "companyWechatId": "0",
+        "companyName": "",
+        "name": "清洗挂机",
+        "type": 1,
+        "status": true,
+        "isServiceShow": false,
+        "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-08/1756371905963358382ff-b0e7-408c-a893-2bdac6c3dfb1.jpg",
+        "goodsNum": 0,
+        "level": 2,
+        "parentId": "1960991889347506178",
+        "sortNum": 0,
+        "createTime": "2025-08-28 17:05:22",
+        "updateBy": "(ab1313131313)家盛茂测试",
+        "updateTime": "2025-08-28 17:10:32",
+        "createBy": "(ab1313131313)家盛茂测试",
+        "del": false,
+        "mainName": "",
+        "mainNumber": "",
+        "smallName": null,
+        "smallNumber": null,
+        "taxNumber": "",
+        "taxPercent": null,
+        "goodsNumber": null,
+        "workOrderType": "INSTALL",
+        "demandTypeId": 0,
+        "demandSmallTypeId": 0,
+        "typeOption": "SIGIN",
+        "typeAttribute": null,
+        "goodsCategoryStr": null,
+        "children": null,
+        "goodsCategoryRelaList": null
+      }
+    ],
+    "goodsCategoryRelaList": null
+  },
+  {
+    "categoryId": "1960991889347506179",
+    "companyWechatId": "0",
+    "companyName": "",
+    "name": "辅材",
+    "type": null,
+    "status": true,
+    "isServiceShow": true,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-08/175637185786609849167-902f-415b-b052-7db96a8fb984.jpg",
+    "goodsNum": 0,
+    "level": 1,
+    "parentId": null,
+    "sortNum": 0,
+    "createTime": "2025-08-28 17:04:35",
+    "updateBy": "(ab1313131313)家盛茂测试",
+    "updateTime": "2025-08-28 17:04:35",
+    "createBy": "(ab1313131313)家盛茂测试",
+    "del": null,
+    "mainName": "",
+    "mainNumber": "",
+    "smallName": null,
+    "smallNumber": null,
+    "taxNumber": "",
+    "taxPercent": null,
+    "goodsNumber": null,
+    "workOrderType": null,
+    "demandTypeId": null,
+    "demandSmallTypeId": null,
+    "typeOption": null,
+    "typeAttribute": null,
+    "goodsCategoryStr": null,
+    "children": [
+      {
+        "categoryId": "1960992090288222210",
+        "companyWechatId": "0",
+        "companyName": "",
+        "name": "铜管",
+        "type": 1,
+        "status": true,
+        "isServiceShow": false,
+        "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-08/1756371905963358382ff-b0e7-408c-a893-2bdac6c3dfb1.jpg",
+        "goodsNum": 0,
+        "level": 2,
+        "parentId": "1960991889347506179",
+        "sortNum": 0,
+        "createTime": "2025-08-28 17:05:22",
+        "updateBy": "(ab1313131313)家盛茂测试",
+        "updateTime": "2025-08-28 17:10:32",
+        "createBy": "(ab1313131313)家盛茂测试",
+        "del": false,
+        "mainName": "",
+        "mainNumber": "",
+        "smallName": null,
+        "smallNumber": null,
+        "taxNumber": "",
+        "taxPercent": null,
+        "goodsNumber": null,
+        "workOrderType": "INSTALL",
+        "demandTypeId": 0,
+        "demandSmallTypeId": 0,
+        "typeOption": "SIGIN",
+        "typeAttribute": null,
+        "goodsCategoryStr": null,
+        "children": null,
+        "goodsCategoryRelaList": null
+      }
+    ],
+    "goodsCategoryRelaList": null
+  },
+  {
+    "categoryId": "1977960358764773377",
+    "companyWechatId": "0",
+    "companyName": "",
+    "name": "配件",
+    "type": null,
+    "status": true,
+    "isServiceShow": true,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760417464090e6bdab31-3612-4d5a-8dda-589add589609.jpeg",
+    "goodsNum": 0,
+    "level": 1,
+    "parentId": null,
+    "sortNum": 0,
+    "createTime": "2025-10-14 12:51:13",
+    "updateBy": "(ab1313131313)家盛茂测试",
+    "updateTime": "2025-10-14 12:51:13",
+    "createBy": "(ab1313131313)家盛茂测试",
+    "del": null,
+    "mainName": "",
+    "mainNumber": "",
+    "smallName": null,
+    "smallNumber": null,
+    "taxNumber": "",
+    "taxPercent": null,
+    "goodsNumber": null,
+    "workOrderType": null,
+    "demandTypeId": null,
+    "demandSmallTypeId": null,
+    "typeOption": null,
+    "typeAttribute": null,
+    "goodsCategoryStr": null,
+    "children": [
+      {
+        "categoryId": "1977960542605312001",
+        "companyWechatId": "0",
+        "companyName": "",
+        "name": "外壳",
+        "type": 1,
+        "status": true,
+        "isServiceShow": false,
+        "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760417493583d9c015fa-1986-41be-81ae-cc7365373861.jpeg",
+        "goodsNum": 0,
+        "level": 2,
+        "parentId": "1977960358764773377",
+        "sortNum": 0,
+        "createTime": "2025-10-14 12:51:57",
+        "updateBy": "(ab1313131313)家盛茂测试",
+        "updateTime": "2025-10-14 12:51:57",
+        "createBy": "(ab1313131313)家盛茂测试",
+        "del": false,
+        "mainName": "",
+        "mainNumber": "",
+        "smallName": null,
+        "smallNumber": null,
+        "taxNumber": "",
+        "taxPercent": null,
+        "goodsNumber": null,
+        "workOrderType": "INSTALL",
+        "demandTypeId": 0,
+        "demandSmallTypeId": 0,
+        "typeOption": "SIGIN",
+        "typeAttribute": null,
+        "goodsCategoryStr": null,
+        "children": null,
+        "goodsCategoryRelaList": null
+      }
+    ],
+    "goodsCategoryRelaList": null
+  },
+  {
+    "categoryId": "1992875815999045633",
+    "companyWechatId": "0",
+    "companyName": "",
+    "name": "冰箱",
+    "type": null,
+    "status": true,
+    "isServiceShow": true,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-11/1763973587955555624b9-274d-4541-935a-69a6f20ca3ae.jpeg",
+    "goodsNum": 0,
+    "level": 1,
+    "parentId": null,
+    "sortNum": 0,
+    "createTime": "2025-11-24 16:39:55",
+    "updateBy": "(admin)admin",
+    "updateTime": "2025-11-24 16:39:55",
+    "createBy": "(admin)admin",
+    "del": null,
+    "mainName": "",
+    "mainNumber": "",
+    "smallName": null,
+    "smallNumber": null,
+    "taxNumber": "",
+    "taxPercent": null,
+    "goodsNumber": null,
+    "workOrderType": null,
+    "demandTypeId": null,
+    "demandSmallTypeId": null,
+    "typeOption": null,
+    "typeAttribute": null,
+    "goodsCategoryStr": null,
+    "children": [],
+    "goodsCategoryRelaList": null
+  }
+]
+
+export const goodsList = [
+  {
+    "goodsId": "1979027585979035649",
+    "goodsType": "COMMON",
+    "goodsName": " 格力(GREE)大1匹冷静王新1级能效变频冷酷外机壁挂式卧室空调挂机 KFR-26GW/(26549)FNhAc-B1 ",
+    "brandId": "1806575879551893506",
+    "brandName": "格力",
+    "describeText": "格力(GREE)大1匹冷静王新1级能效变频冷酷外机壁挂式卧室空调挂机 KFR-26GW/(26549)FNhAc-B1 ",
+    "categoryId": "18316088788948582411807614745054466050",
+    "orgGoodsPrice": 0.00,
+    "goodsPrice": 3299.00,
+    "soldNum": 22,
+    "hasSpec": false,
+    "showDetailStock": true,
+    "showDetailSold": true,
+    "freightType": "TEMPLATE",
+    "freightTemplateId": "",
+    "freight": null,
+    "sortNum": 0,
+    "status": true,
+    "logo": "",
+    "logoStartTime": null,
+    "logoEndTime": null,
+    "useCoupon": 1,
+    "del": false,
+    "createTime": "2025-10-17 11:32:00",
+    "companyWechatId": "1831608878894858241",
+    "companyName": "",
+    "orderSmallType": "",
+    "isVr": false,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760671892271b02e13ba-df5a-4d8c-8d5a-8a34fd9012cb.jpg",
+    "objId": null,
+    "tags1": [
+      "冷酷外机"
+    ],
+    "tags2": [
+      "推荐",
+      "爆款"
+    ],
+    "goodsSpecNewPrice": null
+  },
+  {
+    "goodsId": "1977985365311979521",
+    "goodsType": "COMMON",
+    "goodsName": "配件外壳",
+    "brandId": "1806575879551893506",
+    "brandName": "格力",
+    "describeText": "",
+    "categoryId": "1977960542605312001",
+    "orgGoodsPrice": 0.00,
+    "goodsPrice": 3.00,
+    "soldNum": 0,
+    "hasSpec": false,
+    "showDetailStock": true,
+    "showDetailSold": true,
+    "freightType": "TEMPLATE",
+    "freightTemplateId": "",
+    "freight": null,
+    "sortNum": 0,
+    "status": true,
+    "logo": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760495666651a440a86b-9b6b-48f7-bc9b-5524ab5667ed.png",
+    "logoStartTime": "2025-10-15 00:00:00",
+    "logoEndTime": "2025-10-25 23:59:59",
+    "useCoupon": 1,
+    "del": false,
+    "createTime": "2025-10-14 14:30:35",
+    "companyWechatId": "1831608878894858241",
+    "companyName": "",
+    "orderSmallType": "",
+    "isVr": false,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760423324915fc20f94f-2b6d-479c-a31e-55f3900b1827.jpeg",
+    "objId": null,
+    "tags1": null,
+    "tags2": null,
+    "goodsSpecNewPrice": null
+  },
+  {
+    "goodsId": "1967790526310055937",
+    "goodsType": "COMMON",
+    "goodsName": "铜管10*10*10",
+    "brandId": "1806575879551893506",
+    "brandName": "格力",
+    "describeText": "",
+    "categoryId": "1960992090288222210",
+    "orgGoodsPrice": 0.00,
+    "goodsPrice": 4.00,
+    "soldNum": 0,
+    "hasSpec": false,
+    "showDetailStock": true,
+    "showDetailSold": true,
+    "freightType": "TEMPLATE",
+    "freightTemplateId": "",
+    "freight": null,
+    "sortNum": 0,
+    "status": true,
+    "logo": "",
+    "logoStartTime": null,
+    "logoEndTime": null,
+    "useCoupon": 1,
+    "del": false,
+    "createTime": "2025-09-16 11:19:56",
+    "companyWechatId": "1831608878894858241",
+    "companyName": "",
+    "orderSmallType": "",
+    "isVr": false,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-09/1757990970520f6901910-ce9d-4df1-8752-3a510b499552.png",
+    "objId": null,
+    "tags1": null,
+    "tags2": null,
+    "goodsSpecNewPrice": null
+  },
+  {
+    "goodsId": "1960979734686527490",
+    "goodsType": "COMMON",
+    "goodsName": "空调挂机",
+    "brandId": "1806575879551893506",
+    "brandName": "格力",
+    "describeText": null,
+    "categoryId": "18316088788948582411807614745054466050",
+    "orgGoodsPrice": 0.00,
+    "goodsPrice": 1.00,
+    "soldNum": 0,
+    "hasSpec": false,
+    "showDetailStock": true,
+    "showDetailSold": true,
+    "freightType": "TEMPLATE",
+    "freightTemplateId": "1960979354212823042",
+    "freight": null,
+    "sortNum": 0,
+    "status": true,
+    "logo": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760495337496e4666a79-40b5-4b29-8319-47e200340037.png",
+    "logoStartTime": "2025-10-15 00:00:00",
+    "logoEndTime": "2025-10-25 23:59:59",
+    "useCoupon": 1,
+    "del": false,
+    "createTime": "2025-08-28 16:16:17",
+    "companyWechatId": "1831608878894858241",
+    "companyName": "",
+    "orderSmallType": "18316088788948582411739597889595072513",
+    "isVr": false,
+    "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2024-07/171981829350757d11903-b866-41a5-b42b-e6186bb36821.jpg",
+    "objId": null,
+    "tags1": [
+      "省电"
+    ],
+    "tags2": [
+      "空调",
+      "推荐"
+    ],
+    "goodsSpecNewPrice": null
+  }
+]
+
+export const goodsDetailData = {
+  "goodsId": "1979027585979035649",
+  "goodsType": "COMMON",
+  "goodsName": " 格力(GREE)大1匹冷静王新1级能效变频冷酷外机壁挂式卧室空调挂机 KFR-26GW/(26549)FNhAc-B1 ",
+  "brandId": "1806575879551893506",
+  "brandName": "格力",
+  "describeText": "格力(GREE)大1匹冷静王新1级能效变频冷酷外机壁挂式卧室空调挂机 KFR-26GW/(26549)FNhAc-B1 ",
+  "categoryId": "18316088788948582411807614745054466050",
+  "orgGoodsPrice": 0.00,
+  "goodsPrice": 3299.00,
+  "soldNum": 22,
+  "hasSpec": false,
+  "showDetailStock": true,
+  "showDetailSold": true,
+  "freightType": "TEMPLATE",
+  "freightTemplateId": "",
+  "freight": null,
+  "sortNum": 0,
+  "status": true,
+  "logo": "",
+  "logoStartTime": null,
+  "logoEndTime": null,
+  "useCoupon": 1,
+  "del": false,
+  "createTime": "2025-10-17 11:32:00",
+  "companyWechatId": "1831608878894858241",
+  "companyName": "",
+  "orderSmallType": "",
+  "isVr": false,
+  "content": "<p><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/164782676171109445859-2b58-4588-9838-25f60c7ab2e5.jpg\"><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/1647826761936fd9ca5eb-7f1a-47a3-85b4-68dcae3ee510.jpg\"><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/164782676197961ce5e3c-34de-444d-81b3-b98d77f66dc3.jpg\"><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/1647826761997f3bbc61c-ea3d-4972-9f9e-db144525b971.jpg\"><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/16478267619672ada646b-0ea1-4c78-8eff-d83c4e4822c3.jpg\"><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/1647826761929907056f4-b2de-4ac6-8163-4cdab5a7406b.jpg\"><img src=\"https://zfire-train.oss-cn-guangzhou.aliyuncs.com/train/pic/1647826761815d3da0d4e-6da4-4b83-96fb-b48a7763a1e5.jpg\"></p>",
+  "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760671892271b02e13ba-df5a-4d8c-8d5a-8a34fd9012cb.jpg",
+  "vedio": null,
+  "stock": 0,
+  "goodsSpecs": [
+    {
+      "goodsSpecId": "1979027673379942401",
+      "goodsId": "1979027585979035649",
+      "goodsCode": "1979014046354681857",
+      "name": "大1匹冷静王新1级能效变频冷酷外机壁挂式卧室空调",
+      "specValue": "大1匹冷静王新1级能效变频冷酷外机壁挂式卧室空调",
+      "imgUrl": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/1760671916383af2255c1-51e4-4191-bf33-24b0b0444362.jpg",
+      "costPrice": 0.00,
+      "price": 3299.00,
+      "orgPrice": 0.00,
+      "soldNum": 6,
+      "stockNum": 0,
+      "del": false,
+      "createTime": "2025-10-17 11:32:21",
+      "companyWechatId": "1831608878894858241",
+      "companyName": "家盛茂测试",
+      "mainId": "18316088788948582411806575879648362498",
+      "mainName": "家用空调",
+      "smallId": "18316088788948582411807616035134296065",
+      "smallName": "挂式空调",
+      "unit": "C",
+      "firstFee": 100.00,
+      "continueFee": 0.00,
+      "stockWarningNum": 6,
+      "secKillId": null,
+      "secKillSpecId": null,
+      "secStockNum": null,
+      "limitBuy": null,
+      "endHour": null,
+      "secShareAmount": null,
+      "secPrice": null,
+      "secType": null,
+      "orgGoodsPrice": 0.00,
+      "promotionFullPieceNum": null,
+      "promotionFullPiecePrice": null,
+      "adviceNotice": false,
+      "goodsSpecNewPrice": null
+    }
+  ],
+  "images": [
+    {
+      "id": "1979027489786867713",
+      "objId": "1979027585979035649",
+      "objType": "goods_img_cm",
+      "fileType": "jpg",
+      "url": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-10/17606718966171c775ec4-6a36-4b61-9a16-24ade2e99689.jpg",
+      "smallUrl": "",
+      "name": "8888",
+      "suffix": null,
+      "fileSize": 42294.00,
+      "del": false,
+      "createTime": "2025-11-19 09:52:28"
+    }
+  ],
+  "favorite": false,
+  "limitBuy": null,
+  "commonTemplate": null,
+  "pubCommonTemplate": null,
+  "tags1": [
+    "冷酷外机"
+  ],
+  "tags2": [
+    "推荐",
+    "爆款"
+  ],
+  "goodsSpecNewPrice": null
+}
+
+export const fileList = [
+  {
+    "id": "1990790270580051970",
+    "goodsId": "1960979734686527490",
+    "goodsDocumentsId": "1990594732488384514",
+    "folderName": "11111",
+    "commonFileId": "1990594928341409793",
+    "fileName": "2023年安装维修作业服务承揽协议(广东家盛茂)-佛山thttrrk17tba6vqmqkph7z8cewtxcjgt20241211163236",
+    "url": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-11/1763429786649336b1165-e6eb-4c39-b18f-c61b01c70844.pdf"
+  },
+  {
+    "id": "1990790270580051971",
+    "goodsId": "1960979734686527490",
+    "goodsDocumentsId": "1990594732488384514",
+    "folderName": "11111",
+    "commonFileId": "1990594807918747650",
+    "fileName": "invoice-3",
+    "url": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-11/1763429760174a3d3246d-8a0c-4a1c-94ff-bc412e436bfd.pdf"
+  },
+  {
+    "id": "1990790270580051972",
+    "goodsId": "1960979734686527490",
+    "goodsDocumentsId": "1989540020527730690",
+    "folderName": "套件1文档",
+    "commonFileId": "1989578716929003521",
+    "fileName": "套件文档1",
+    "url": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-11/176318750523741a23fc6-ef97-4c7a-af09-988f7a6a9867.pdf"
+  },
+  {
+    "id": "1990790270584246273",
+    "goodsId": "1960979734686527490",
+    "goodsDocumentsId": "1989540020527730690",
+    "folderName": "套件1文档",
+    "commonFileId": "1989578964116115458",
+    "fileName": "套件文档3",
+    "url": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-11/176318756417681df3e32-329c-477c-b354-686637d81105.pdf"
+  },
+  {
+    "id": "1990790270584246274",
+    "goodsId": "1960979734686527490",
+    "goodsDocumentsId": "1987507789785370626",
+    "folderName": "A-folder1",
+    "commonFileId": "1988260252951924737",
+    "fileName": "测试PDF",
+    "url": "https://zf-mall-test.oss-cn-shenzhen.aliyuncs.com/2025-11/17628731589340272b75b-d71c-4d4c-9931-475e4d9aca4d.pdf"
+  }
+]

+ 88 - 0
src/utils/request.js

@@ -0,0 +1,88 @@
+import axios from 'axios';
+import { getToken, removeToken } from '@/utils/token';
+import { createVNode } from 'vue';
+import { message, Modal } from 'ant-design-vue';
+import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
+import { useUserStore } from '@/store/user';
+import { useRouter } from 'vue-router';
+
+const service = axios.create({
+  baseURL: import.meta.env.VITE_APP_BASE_API,
+  // withCredentials: true,
+  timeout: 300000
+})
+const whiteCodes = [200, 201, 4444];
+
+const showConfirm = (options = {}) => {
+  Modal.confirm({
+    title: options.title,
+    icon: createVNode(ExclamationCircleOutlined),
+    content: options.content,
+    okText: options.okText || '确定',
+    cancelText: options.cancelText || '取消',
+    onOk() {
+      options.onOk && options.onOk();
+    },
+    onCancel() {
+      options.onCancel && options.onCancel();
+    }
+  });
+}
+
+service.interceptors.request.use(
+  config => {
+    if (config.json) {
+      config.headers['Content-Type'] = 'application/json'
+    } else {
+      config.headers['Content-Type'] = 'application/x-www-form-urlencoded'
+    }
+    if (getToken()) {
+      config.headers['x-token'] = getToken();
+    }
+    return config
+  },
+  error => {
+    return Promise.reject(error)
+  }
+)
+
+service.interceptors.response.use(response => {
+    const res = response.data
+    if (whiteCodes.indexOf(res.code) < 0) {
+      if (JSON.parse(response.config.data || '{}')?.returnErr || response.config.params?.returnErr) {
+        return Promise.reject(res)
+      }
+      message.error(res.message || 'Error');
+      if (res.code === 1001) {
+        showConfirm({
+          title: '登录失效',
+          content: '登录失效,您可以取消停留在此页面,或重新登录',
+          okText: '重新登录',
+          cancelText: '取消',
+          onOk: () => {
+            // 重置token, 重置存在cookie里面的userId并且跳转到登录页
+            removeToken();
+            useUserStore().clearUserInfo();
+            window.location.href = '/login'
+          }        
+        })
+      } else if (res.code === 4004) {
+        showConfirm({
+          title: '账号过期',
+          content: '账号过期,请前往续费',
+          okText: '去续费',
+          cancelText: '取消',
+          onOk: () => {}        
+        })
+      }
+      return Promise.reject(new Error(res.message || 'Error'))
+    }
+    return Promise.resolve(res)
+  },
+  error => {
+    message.error(res.message || 'Error');
+    return Promise.reject(error)
+  }
+)
+
+export default service

+ 224 - 0
src/utils/text-processor.js

@@ -0,0 +1,224 @@
+// text-processor.js
+import axios from 'axios'
+import { delayPerform } from 'js-perform-lock'
+
+const WindowsTranslateApi = 'https://jiasm.zfire.top/translate'
+
+// 数据过滤校验
+function isNumberRegex(str) {
+  const regexPatterns = [
+    /^\d+$/, // 纯数字
+    /^\d*\.\d+$/, // 浮点数
+    /^([01]\d|2[0-3]):([0-5]\d)$/, // 时间 (HH:MM)
+    /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/, // 时间 (HH:MM:SS)
+    /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, // 完整时间戳
+    /^[A-Za-z]+$/, // 纯字母
+    /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]+$/, // 字母和数字
+    /^(?=.*[A-Za-z])(?=.*[\p{P}\p{S}])[\p{L}\p{P}\p{S}]+$/u, // 字母和标点符号
+    /^(?=.*\d)(?=.*[\p{P}\p{S}])[\d\p{P}\p{S}]+$/u, // 数字和标点符号
+    /^[\p{P}\s\dA-Za-z]+$/u // 字母、数字和标点符号
+  ]
+  return regexPatterns.some(pattern => pattern.test(str)) || !!window.Vue_Translation_Of_Text_Old_Data?.[str]
+}
+
+// 文本转译请求
+function pushNewText(data) {
+  return new Promise(function (resolve, reject) {
+    axios
+      .post(`${WindowsTranslateApi}/api/v1/common/translationOfText`, data)
+      .then(response => {
+        if (response.data.code === 0) {
+          const obj = {}
+          const obj2 = {}
+          response.data.data.forEach(val => {
+            obj[val[0]] = val[1]
+            obj2[val[1]] = true
+          })
+          window.Vue_Translation_Of_Text_Old_Data = { 
+            ...(window.Vue_Translation_Of_Text_Old_Data || {}), 
+            ...obj2 
+          }
+          window.Vue_Translation_Of_Text_Data = { 
+            ...(window.Vue_Translation_Of_Text_Data || {}), 
+            ...obj 
+          }
+          resolve(obj)
+        } else {
+          reject(new Error('API returned non-zero code'))
+        }
+      })
+      .catch(reject)
+  })
+}
+
+// 延迟替换处理器
+const deferredReplacement = (function () {
+  const allKeywords = []
+  const keywords = []
+  const callbacks = []
+  let keywordIndex = 0
+  let callbackIndex = 0
+
+  const delayProcessor = new delayPerform(250).refactor(function () {
+    const currentKeywords = keywords.splice(0, keywordIndex)
+    const currentCallbacks = callbacks.splice(0, callbackIndex)
+    keywordIndex -= currentKeywords.length
+    callbackIndex -= currentCallbacks.length
+    
+    if (currentKeywords.length > 0) {
+      pushNewText({
+        keywords: currentKeywords,
+        targetLanguage: window?.Vue_Translation_Of_Text_Type
+      }).then(() => {
+        currentCallbacks.forEach(cb => cb?.())
+      }).catch(error => {
+        console.error('Translation failed:', error)
+        currentCallbacks.forEach(cb => cb?.())
+      })
+    } else if (currentCallbacks.length > 0) {
+      setTimeout(() => {
+        currentCallbacks.forEach(cb => cb?.())
+      }, 250)
+    }
+  })
+
+  return function (text, callback) {
+    delayProcessor()
+    if (!allKeywords.includes(text)) {
+      keywordIndex++
+      allKeywords.push(text)
+      keywords.push(text)
+    }
+    callbackIndex++
+    callbacks.push(callback)
+  }
+})()
+
+// DOM 处理器
+export const domProcessor = {
+  // 处理文本节点
+  processTextNode(node) {
+    const text = node.textContent?.trim()
+    if (!text || isNumberRegex(text)) return
+
+    try {
+      if (window.Vue_Translation_Of_Text_Data?.[text]) {
+        node.textContent = window.Vue_Translation_Of_Text_Data[text]
+      } else {
+        deferredReplacement(text, () => {
+          if (window.Vue_Translation_Of_Text_Data?.[text]) {
+            node.textContent = window.Vue_Translation_Of_Text_Data[text]
+          }
+        })
+      }
+    } catch (error) {
+      console.error('Text processing error:', error)
+    }
+  },
+
+  // 处理输入框 placeholder
+  processInputPlaceholder(element) {
+    const placeholder = element.getAttribute('placeholder')?.trim()
+    if (!placeholder || isNumberRegex(placeholder)) return
+
+    try {
+      if (window.Vue_Translation_Of_Text_Data?.[placeholder]) {
+        element.setAttribute('placeholder', window.Vue_Translation_Of_Text_Data[placeholder])
+      } else {
+        deferredReplacement(placeholder, () => {
+          if (window.Vue_Translation_Of_Text_Data?.[placeholder]) {
+            element.setAttribute('placeholder', window.Vue_Translation_Of_Text_Data[placeholder])
+          }
+        })
+      }
+    } catch (error) {
+      console.error('Placeholder processing error:', error)
+    }
+  },
+
+  // 处理输入框值
+  processInputValue(element) {
+    const value = element.value?.trim()
+    if (!value || isNumberRegex(value)) return
+
+    try {
+      if (window.Vue_Translation_Of_Text_Data?.[value]) {
+        element.value = window.Vue_Translation_Of_Text_Data[value]
+      } else {
+        deferredReplacement(value, () => {
+          if (window.Vue_Translation_Of_Text_Data?.[value]) {
+            element.value = window.Vue_Translation_Of_Text_Data[value]
+          }
+        })
+      }
+    } catch (error) {
+      console.error('Input value processing error:', error)
+    }
+  },
+
+  // 处理属性文本
+  processAttributeText(element, attributeName) {
+    const value = element.getAttribute(attributeName)?.trim()
+    if (!value || isNumberRegex(value)) return
+
+    try {
+      if (window.Vue_Translation_Of_Text_Data?.[value]) {
+        element.setAttribute(attributeName, window.Vue_Translation_Of_Text_Data[value])
+      } else {
+        deferredReplacement(value, () => {
+          if (window.Vue_Translation_Of_Text_Data?.[value]) {
+            element.setAttribute(attributeName, window.Vue_Translation_Of_Text_Data[value])
+          }
+        })
+      }
+    } catch (error) {
+      console.error(`Attribute ${attributeName} processing error:`, error)
+    }
+  },
+
+  // 递归处理 DOM 元素
+  processElement(element) {
+    if (element && element.nodeType === Node.ELEMENT_NODE) {
+      // 跳过 table 中的 td 元素
+      // if (element.nodeName.toLowerCase() === 'td') {
+      //   return
+      // }
+
+      // 处理输入框和文本域
+      if (['INPUT', 'TEXTAREA', 'SELECT'].includes(element.nodeName)) {
+        this.processInputPlaceholder(element)
+        if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
+          this.processInputValue(element)
+        }
+      }
+
+      // 处理 Ant Design 组件属性
+      this.processAttributeText(element, 'title')
+      this.processAttributeText(element, 'aria-label')
+      this.processAttributeText(element, 'alt')
+
+      // 处理子节点
+      Array.from(element.childNodes).forEach(child => {
+        if (child.nodeType === Node.TEXT_NODE) {
+          this.processTextNode(child)
+        } else {
+          this.processElement(child)
+        }
+      })
+    }
+  },
+
+  // 处理特定选择器元素
+  processSelector(selector) {
+    Array.from(document.querySelectorAll(selector)).forEach(element => {
+      this.processElement(element)
+    })
+  },
+
+  // 处理标签名元素
+  processTagName(tagName) {
+    Array.from(document.getElementsByTagName(tagName)).forEach(element => {
+      this.processElement(element)
+    })
+  }
+}

+ 12 - 0
src/utils/token.js

@@ -0,0 +1,12 @@
+// 处理token
+export const setToken = (token) => {
+  window.sessionStorage.setItem('TOKEN', token)
+}
+
+export const getToken = () => {
+  return window.sessionStorage.getItem('TOKEN');
+}
+
+export const removeToken = () => {
+  window.sessionStorage.removeItem('TOKEN')
+}

+ 96 - 0
src/utils/vue3-plugin.js

@@ -0,0 +1,96 @@
+// vue3-plugin.js
+import { inject } from 'vue'
+import { domProcessor } from './text-processor'
+import { antdProcessor } from './antd-processor'
+
+export const GlobalTextProcessor = {
+  install(app) {
+    // 启动 Ant Design 处理器
+    antdProcessor.initObserver()
+
+    // 添加全局方法
+    app.config.globalProperties.$processTextNodes = function (element = this.$el) {
+      domProcessor.processElement(element)
+      antdProcessor.processAntdComponents()
+    }
+
+    app.config.globalProperties.$autoSelect = function (className) {
+      domProcessor.processSelector(`.${className}`)
+    }
+
+    app.config.globalProperties.$autoHtmlSelect = function (tagName) {
+      domProcessor.processTagName(tagName)
+    }
+
+    // 添加 Ant Design 特定方法
+    app.config.globalProperties.$processAntdComponents = function () {
+      antdProcessor.processAntdComponents()
+    }
+
+    // 使用指令方式
+    app.directive('translate', {
+      mounted(el) {
+        domProcessor.processElement(el)
+        antdProcessor.processAntdComponent(el)
+      },
+      updated(el) {
+        domProcessor.processElement(el)
+        antdProcessor.processAntdComponent(el)
+      }
+    })
+
+    // 使用 mixin
+    app.mixin({
+      updated() {
+        this.$nextTick(() => {
+          this.$processTextNodes(this.$el)
+          this.$autoHtmlSelect('title')
+        })
+      },
+      mounted() {
+        this.$nextTick(() => {
+          this.$processAntdComponents()
+        })
+      }
+    })
+
+    // 提供 Composition API
+    app.provide('textProcessor', {
+      processTextNodes: (element) => {
+        domProcessor.processElement(element)
+        antdProcessor.processAntdComponents()
+      },
+      processAntdComponents: () => antdProcessor.processAntdComponents(),
+      autoSelect: (className) => domProcessor.processSelector(`.${className}`),
+      autoHtmlSelect: (tagName) => domProcessor.processTagName(tagName)
+    })
+  }
+}
+
+// Composition API
+export function useTextProcessor() {
+  const injector = inject('textProcessor', null)
+  
+  const processTextNodes = (element) => {
+    injector?.processTextNodes(element)
+  }
+
+  const processAntdComponents = () => {
+    injector?.processAntdComponents()
+  }
+
+  const autoSelect = (className) => {
+    injector?.autoSelect(className)
+  }
+
+  const autoHtmlSelect = (tagName) => {
+    injector?.autoHtmlSelect(tagName)
+  }
+
+  return {
+    processTextNodes,
+    processAntdComponents,
+    autoSelect,
+    autoHtmlSelect
+  }
+}

+ 47 - 0
vite.config.js

@@ -0,0 +1,47 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import path from 'path'
+import Components from 'unplugin-vue-components/vite'
+import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
+
+export default defineConfig({
+  plugins: [
+    vue(),
+    Components({
+      resolvers: [
+        AntDesignVueResolver({
+          importStyle: false, // css in js
+        })
+      ],
+    })
+  ],
+  resolve: {
+    alias: {
+      '@': path.resolve(__dirname, 'src')
+    }
+  },
+  css: {
+    preprocessorOptions: {
+      less: {
+        math: 'always',
+        globalVars: {},
+        modifyVars: {
+          'hack': 'true; @import "@/styles/variables.less";'
+        }
+      }
+    }
+  },
+  server: {
+    port: 8088,
+    open: true,
+    proxy: {
+      // '/api': {
+      //   // target: 'https://jiasm.zfire.top/zfdapi/',
+      //   target: 'https://jiasm.zfire.top',
+      //   ws: true,
+      //   changeOrigin: true,
+      //   rewrite: (path) => path.replace(/^\/api/, '')
+      // }
+    }
+  }
+})