Commit 8a06965a6874e231b8e63a18342d35b5c1cb6c5c

Authored by 杨刚
0 parents

init

.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +# Logs
  2 +logs
  3 +*.log
  4 +npm-debug.log*
  5 +yarn-debug.log*
  6 +yarn-error.log*
  7 +pnpm-debug.log*
  8 +lerna-debug.log*
  9 +
  10 +node_modules
  11 +dist
  12 +dist-ssr
  13 +*.local
  14 +
  15 +# Editor directories and files
  16 +.vscode/*
  17 +!.vscode/extensions.json
  18 +.idea
  19 +.DS_Store
  20 +*.suo
  21 +*.ntvs*
  22 +*.njsproj
  23 +*.sln
  24 +*.sw?
... ...
.vscode/extensions.json 0 → 100644
  1 +++ a/.vscode/extensions.json
  1 +{
  2 + "recommendations": ["Vue.volar"]
  3 +}
... ...
README.md 0 → 100644
  1 +++ a/README.md
  1 +# Vue 3 + TypeScript + Vite
  2 +
  3 +This template should help get you started developing with Vue 3 and TypeScript 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.
  4 +
  5 +Learn more about the recommended Project Setup and IDE Support in the [Vue Docs TypeScript Guide](https://vuejs.org/guide/typescript/overview.html#project-setup).
... ...
index.html 0 → 100644
  1 +++ a/index.html
  1 +<!doctype html>
  2 +<html lang="en">
  3 + <head>
  4 + <meta charset="UTF-8" />
  5 + <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
  6 + <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  7 + <title>rider-admin</title>
  8 + </head>
  9 + <body>
  10 + <div id="app"></div>
  11 + <script type="module" src="/src/main.ts"></script>
  12 + </body>
  13 +</html>
... ...
package-lock.json 0 → 100644
  1 +++ a/package-lock.json
  1 +{
  2 + "name": "rider-admin",
  3 + "version": "0.0.0",
  4 + "lockfileVersion": 3,
  5 + "requires": true,
  6 + "packages": {
  7 + "": {
  8 + "name": "rider-admin",
  9 + "version": "0.0.0",
  10 + "dependencies": {
  11 + "@ant-design/icons-vue": "^7.0.1",
  12 + "ant-design-vue": "^4.2.6",
  13 + "axios": "^1.13.6",
  14 + "dayjs": "^1.11.20",
  15 + "pinia": "^3.0.4",
  16 + "vue": "^3.5.30",
  17 + "vue-router": "^4.6.4"
  18 + },
  19 + "devDependencies": {
  20 + "@types/node": "^24.12.0",
  21 + "@vitejs/plugin-vue": "^6.0.5",
  22 + "@vue/tsconfig": "^0.9.0",
  23 + "typescript": "~5.9.3",
  24 + "vite": "^8.0.1",
  25 + "vue-tsc": "^3.2.5"
  26 + }
  27 + },
  28 + "node_modules/@ant-design/colors": {
  29 + "version": "6.0.0",
  30 + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz",
  31 + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
  32 + "license": "MIT",
  33 + "dependencies": {
  34 + "@ctrl/tinycolor": "^3.4.0"
  35 + }
  36 + },
  37 + "node_modules/@ant-design/icons-svg": {
  38 + "version": "4.4.2",
  39 + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
  40 + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
  41 + "license": "MIT"
  42 + },
  43 + "node_modules/@ant-design/icons-vue": {
  44 + "version": "7.0.1",
  45 + "resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz",
  46 + "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==",
  47 + "license": "MIT",
  48 + "dependencies": {
  49 + "@ant-design/colors": "^6.0.0",
  50 + "@ant-design/icons-svg": "^4.2.1"
  51 + },
  52 + "peerDependencies": {
  53 + "vue": ">=3.0.3"
  54 + }
  55 + },
  56 + "node_modules/@babel/helper-string-parser": {
  57 + "version": "7.27.1",
  58 + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
  59 + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
  60 + "license": "MIT",
  61 + "engines": {
  62 + "node": ">=6.9.0"
  63 + }
  64 + },
  65 + "node_modules/@babel/helper-validator-identifier": {
  66 + "version": "7.28.5",
  67 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
  68 + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
  69 + "license": "MIT",
  70 + "engines": {
  71 + "node": ">=6.9.0"
  72 + }
  73 + },
  74 + "node_modules/@babel/parser": {
  75 + "version": "7.29.2",
  76 + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz",
  77 + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==",
  78 + "license": "MIT",
  79 + "dependencies": {
  80 + "@babel/types": "^7.29.0"
  81 + },
  82 + "bin": {
  83 + "parser": "bin/babel-parser.js"
  84 + },
  85 + "engines": {
  86 + "node": ">=6.0.0"
  87 + }
  88 + },
  89 + "node_modules/@babel/runtime": {
  90 + "version": "7.29.2",
  91 + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
  92 + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
  93 + "license": "MIT",
  94 + "engines": {
  95 + "node": ">=6.9.0"
  96 + }
  97 + },
  98 + "node_modules/@babel/types": {
  99 + "version": "7.29.0",
  100 + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
  101 + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
  102 + "license": "MIT",
  103 + "dependencies": {
  104 + "@babel/helper-string-parser": "^7.27.1",
  105 + "@babel/helper-validator-identifier": "^7.28.5"
  106 + },
  107 + "engines": {
  108 + "node": ">=6.9.0"
  109 + }
  110 + },
  111 + "node_modules/@ctrl/tinycolor": {
  112 + "version": "3.6.1",
  113 + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
  114 + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
  115 + "license": "MIT",
  116 + "engines": {
  117 + "node": ">=10"
  118 + }
  119 + },
  120 + "node_modules/@emnapi/core": {
  121 + "version": "1.9.1",
  122 + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz",
  123 + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==",
  124 + "dev": true,
  125 + "license": "MIT",
  126 + "optional": true,
  127 + "dependencies": {
  128 + "@emnapi/wasi-threads": "1.2.0",
  129 + "tslib": "^2.4.0"
  130 + }
  131 + },
  132 + "node_modules/@emnapi/runtime": {
  133 + "version": "1.9.1",
  134 + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
  135 + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==",
  136 + "dev": true,
  137 + "license": "MIT",
  138 + "optional": true,
  139 + "dependencies": {
  140 + "tslib": "^2.4.0"
  141 + }
  142 + },
  143 + "node_modules/@emnapi/wasi-threads": {
  144 + "version": "1.2.0",
  145 + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
  146 + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
  147 + "dev": true,
  148 + "license": "MIT",
  149 + "optional": true,
  150 + "dependencies": {
  151 + "tslib": "^2.4.0"
  152 + }
  153 + },
  154 + "node_modules/@emotion/hash": {
  155 + "version": "0.9.2",
  156 + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
  157 + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
  158 + "license": "MIT"
  159 + },
  160 + "node_modules/@emotion/unitless": {
  161 + "version": "0.8.1",
  162 + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
  163 + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
  164 + "license": "MIT"
  165 + },
  166 + "node_modules/@jridgewell/sourcemap-codec": {
  167 + "version": "1.5.5",
  168 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
  169 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
  170 + "license": "MIT"
  171 + },
  172 + "node_modules/@napi-rs/wasm-runtime": {
  173 + "version": "1.1.1",
  174 + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
  175 + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==",
  176 + "dev": true,
  177 + "license": "MIT",
  178 + "optional": true,
  179 + "dependencies": {
  180 + "@emnapi/core": "^1.7.1",
  181 + "@emnapi/runtime": "^1.7.1",
  182 + "@tybys/wasm-util": "^0.10.1"
  183 + },
  184 + "funding": {
  185 + "type": "github",
  186 + "url": "https://github.com/sponsors/Brooooooklyn"
  187 + }
  188 + },
  189 + "node_modules/@oxc-project/types": {
  190 + "version": "0.122.0",
  191 + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz",
  192 + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==",
  193 + "dev": true,
  194 + "license": "MIT",
  195 + "funding": {
  196 + "url": "https://github.com/sponsors/Boshen"
  197 + }
  198 + },
  199 + "node_modules/@rolldown/binding-android-arm64": {
  200 + "version": "1.0.0-rc.11",
  201 + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.11.tgz",
  202 + "integrity": "sha512-SJ+/g+xNnOh6NqYxD0V3uVN4W3VfnrGsC9/hoglicgTNfABFG9JjISvkkU0dNY84MNHLWyOgxP9v9Y9pX4S7+A==",
  203 + "cpu": [
  204 + "arm64"
  205 + ],
  206 + "dev": true,
  207 + "license": "MIT",
  208 + "optional": true,
  209 + "os": [
  210 + "android"
  211 + ],
  212 + "engines": {
  213 + "node": "^20.19.0 || >=22.12.0"
  214 + }
  215 + },
  216 + "node_modules/@rolldown/binding-darwin-arm64": {
  217 + "version": "1.0.0-rc.11",
  218 + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.11.tgz",
  219 + "integrity": "sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==",
  220 + "cpu": [
  221 + "arm64"
  222 + ],
  223 + "dev": true,
  224 + "license": "MIT",
  225 + "optional": true,
  226 + "os": [
  227 + "darwin"
  228 + ],
  229 + "engines": {
  230 + "node": "^20.19.0 || >=22.12.0"
  231 + }
  232 + },
  233 + "node_modules/@rolldown/binding-darwin-x64": {
  234 + "version": "1.0.0-rc.11",
  235 + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.11.tgz",
  236 + "integrity": "sha512-39Ks6UvIHq4rEogIfQBoBRusj0Q0nPVWIvqmwBLaT6aqQGIakHdESBVOPRRLacy4WwUPIx4ZKzfZ9PMW+IeyUQ==",
  237 + "cpu": [
  238 + "x64"
  239 + ],
  240 + "dev": true,
  241 + "license": "MIT",
  242 + "optional": true,
  243 + "os": [
  244 + "darwin"
  245 + ],
  246 + "engines": {
  247 + "node": "^20.19.0 || >=22.12.0"
  248 + }
  249 + },
  250 + "node_modules/@rolldown/binding-freebsd-x64": {
  251 + "version": "1.0.0-rc.11",
  252 + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.11.tgz",
  253 + "integrity": "sha512-jfsm0ZHfhiqrvWjJAmzsqiIFPz5e7mAoCOPBNTcNgkiid/LaFKiq92+0ojH+nmJmKYkre4t71BWXUZDNp7vsag==",
  254 + "cpu": [
  255 + "x64"
  256 + ],
  257 + "dev": true,
  258 + "license": "MIT",
  259 + "optional": true,
  260 + "os": [
  261 + "freebsd"
  262 + ],
  263 + "engines": {
  264 + "node": "^20.19.0 || >=22.12.0"
  265 + }
  266 + },
  267 + "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
  268 + "version": "1.0.0-rc.11",
  269 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.11.tgz",
  270 + "integrity": "sha512-zjQaUtSyq1nVe3nxmlSCuR96T1LPlpvmJ0SZy0WJFEsV4kFbXcq2u68L4E6O0XeFj4aex9bEauqjW8UQBeAvfQ==",
  271 + "cpu": [
  272 + "arm"
  273 + ],
  274 + "dev": true,
  275 + "license": "MIT",
  276 + "optional": true,
  277 + "os": [
  278 + "linux"
  279 + ],
  280 + "engines": {
  281 + "node": "^20.19.0 || >=22.12.0"
  282 + }
  283 + },
  284 + "node_modules/@rolldown/binding-linux-arm64-gnu": {
  285 + "version": "1.0.0-rc.11",
  286 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.11.tgz",
  287 + "integrity": "sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==",
  288 + "cpu": [
  289 + "arm64"
  290 + ],
  291 + "dev": true,
  292 + "libc": [
  293 + "glibc"
  294 + ],
  295 + "license": "MIT",
  296 + "optional": true,
  297 + "os": [
  298 + "linux"
  299 + ],
  300 + "engines": {
  301 + "node": "^20.19.0 || >=22.12.0"
  302 + }
  303 + },
  304 + "node_modules/@rolldown/binding-linux-arm64-musl": {
  305 + "version": "1.0.0-rc.11",
  306 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.11.tgz",
  307 + "integrity": "sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==",
  308 + "cpu": [
  309 + "arm64"
  310 + ],
  311 + "dev": true,
  312 + "libc": [
  313 + "musl"
  314 + ],
  315 + "license": "MIT",
  316 + "optional": true,
  317 + "os": [
  318 + "linux"
  319 + ],
  320 + "engines": {
  321 + "node": "^20.19.0 || >=22.12.0"
  322 + }
  323 + },
  324 + "node_modules/@rolldown/binding-linux-ppc64-gnu": {
  325 + "version": "1.0.0-rc.11",
  326 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.11.tgz",
  327 + "integrity": "sha512-ZlFgw46NOAGMgcdvdYwAGu2Q+SLFA9LzbJLW+iyMOJyhj5wk6P3KEE9Gct4xWwSzFoPI7JCdYmYMzVtlgQ+zfw==",
  328 + "cpu": [
  329 + "ppc64"
  330 + ],
  331 + "dev": true,
  332 + "libc": [
  333 + "glibc"
  334 + ],
  335 + "license": "MIT",
  336 + "optional": true,
  337 + "os": [
  338 + "linux"
  339 + ],
  340 + "engines": {
  341 + "node": "^20.19.0 || >=22.12.0"
  342 + }
  343 + },
  344 + "node_modules/@rolldown/binding-linux-s390x-gnu": {
  345 + "version": "1.0.0-rc.11",
  346 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.11.tgz",
  347 + "integrity": "sha512-hIOYmuT6ofM4K04XAZd3OzMySEO4K0/nc9+jmNcxNAxRi6c5UWpqfw3KMFV4MVFWL+jQsSh+bGw2VqmaPMTLyw==",
  348 + "cpu": [
  349 + "s390x"
  350 + ],
  351 + "dev": true,
  352 + "libc": [
  353 + "glibc"
  354 + ],
  355 + "license": "MIT",
  356 + "optional": true,
  357 + "os": [
  358 + "linux"
  359 + ],
  360 + "engines": {
  361 + "node": "^20.19.0 || >=22.12.0"
  362 + }
  363 + },
  364 + "node_modules/@rolldown/binding-linux-x64-gnu": {
  365 + "version": "1.0.0-rc.11",
  366 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.11.tgz",
  367 + "integrity": "sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==",
  368 + "cpu": [
  369 + "x64"
  370 + ],
  371 + "dev": true,
  372 + "libc": [
  373 + "glibc"
  374 + ],
  375 + "license": "MIT",
  376 + "optional": true,
  377 + "os": [
  378 + "linux"
  379 + ],
  380 + "engines": {
  381 + "node": "^20.19.0 || >=22.12.0"
  382 + }
  383 + },
  384 + "node_modules/@rolldown/binding-linux-x64-musl": {
  385 + "version": "1.0.0-rc.11",
  386 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.11.tgz",
  387 + "integrity": "sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==",
  388 + "cpu": [
  389 + "x64"
  390 + ],
  391 + "dev": true,
  392 + "libc": [
  393 + "musl"
  394 + ],
  395 + "license": "MIT",
  396 + "optional": true,
  397 + "os": [
  398 + "linux"
  399 + ],
  400 + "engines": {
  401 + "node": "^20.19.0 || >=22.12.0"
  402 + }
  403 + },
  404 + "node_modules/@rolldown/binding-openharmony-arm64": {
  405 + "version": "1.0.0-rc.11",
  406 + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.11.tgz",
  407 + "integrity": "sha512-mcp3Rio2w72IvdZG0oQ4bM2c2oumtwHfUfKncUM6zGgz0KgPz4YmDPQfnXEiY5t3+KD/i8HG2rOB/LxdmieK2g==",
  408 + "cpu": [
  409 + "arm64"
  410 + ],
  411 + "dev": true,
  412 + "license": "MIT",
  413 + "optional": true,
  414 + "os": [
  415 + "openharmony"
  416 + ],
  417 + "engines": {
  418 + "node": "^20.19.0 || >=22.12.0"
  419 + }
  420 + },
  421 + "node_modules/@rolldown/binding-wasm32-wasi": {
  422 + "version": "1.0.0-rc.11",
  423 + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.11.tgz",
  424 + "integrity": "sha512-LXk5Hii1Ph9asuGRjBuz8TUxdc1lWzB7nyfdoRgI0WGPZKmCxvlKk8KfYysqtr4MfGElu/f/pEQRh8fcEgkrWw==",
  425 + "cpu": [
  426 + "wasm32"
  427 + ],
  428 + "dev": true,
  429 + "license": "MIT",
  430 + "optional": true,
  431 + "dependencies": {
  432 + "@napi-rs/wasm-runtime": "^1.1.1"
  433 + },
  434 + "engines": {
  435 + "node": ">=14.0.0"
  436 + }
  437 + },
  438 + "node_modules/@rolldown/binding-win32-arm64-msvc": {
  439 + "version": "1.0.0-rc.11",
  440 + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.11.tgz",
  441 + "integrity": "sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==",
  442 + "cpu": [
  443 + "arm64"
  444 + ],
  445 + "dev": true,
  446 + "license": "MIT",
  447 + "optional": true,
  448 + "os": [
  449 + "win32"
  450 + ],
  451 + "engines": {
  452 + "node": "^20.19.0 || >=22.12.0"
  453 + }
  454 + },
  455 + "node_modules/@rolldown/binding-win32-x64-msvc": {
  456 + "version": "1.0.0-rc.11",
  457 + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.11.tgz",
  458 + "integrity": "sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==",
  459 + "cpu": [
  460 + "x64"
  461 + ],
  462 + "dev": true,
  463 + "license": "MIT",
  464 + "optional": true,
  465 + "os": [
  466 + "win32"
  467 + ],
  468 + "engines": {
  469 + "node": "^20.19.0 || >=22.12.0"
  470 + }
  471 + },
  472 + "node_modules/@rolldown/pluginutils": {
  473 + "version": "1.0.0-rc.2",
  474 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz",
  475 + "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==",
  476 + "dev": true,
  477 + "license": "MIT"
  478 + },
  479 + "node_modules/@simonwep/pickr": {
  480 + "version": "1.8.2",
  481 + "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz",
  482 + "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==",
  483 + "license": "MIT",
  484 + "dependencies": {
  485 + "core-js": "^3.15.1",
  486 + "nanopop": "^2.1.0"
  487 + }
  488 + },
  489 + "node_modules/@tybys/wasm-util": {
  490 + "version": "0.10.1",
  491 + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
  492 + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
  493 + "dev": true,
  494 + "license": "MIT",
  495 + "optional": true,
  496 + "dependencies": {
  497 + "tslib": "^2.4.0"
  498 + }
  499 + },
  500 + "node_modules/@types/node": {
  501 + "version": "24.12.0",
  502 + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz",
  503 + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==",
  504 + "dev": true,
  505 + "license": "MIT",
  506 + "dependencies": {
  507 + "undici-types": "~7.16.0"
  508 + }
  509 + },
  510 + "node_modules/@vitejs/plugin-vue": {
  511 + "version": "6.0.5",
  512 + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz",
  513 + "integrity": "sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==",
  514 + "dev": true,
  515 + "license": "MIT",
  516 + "dependencies": {
  517 + "@rolldown/pluginutils": "1.0.0-rc.2"
  518 + },
  519 + "engines": {
  520 + "node": "^20.19.0 || >=22.12.0"
  521 + },
  522 + "peerDependencies": {
  523 + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
  524 + "vue": "^3.2.25"
  525 + }
  526 + },
  527 + "node_modules/@volar/language-core": {
  528 + "version": "2.4.28",
  529 + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz",
  530 + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==",
  531 + "dev": true,
  532 + "license": "MIT",
  533 + "dependencies": {
  534 + "@volar/source-map": "2.4.28"
  535 + }
  536 + },
  537 + "node_modules/@volar/source-map": {
  538 + "version": "2.4.28",
  539 + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz",
  540 + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==",
  541 + "dev": true,
  542 + "license": "MIT"
  543 + },
  544 + "node_modules/@volar/typescript": {
  545 + "version": "2.4.28",
  546 + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz",
  547 + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==",
  548 + "dev": true,
  549 + "license": "MIT",
  550 + "dependencies": {
  551 + "@volar/language-core": "2.4.28",
  552 + "path-browserify": "^1.0.1",
  553 + "vscode-uri": "^3.0.8"
  554 + }
  555 + },
  556 + "node_modules/@vue/compiler-core": {
  557 + "version": "3.5.30",
  558 + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz",
  559 + "integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==",
  560 + "license": "MIT",
  561 + "dependencies": {
  562 + "@babel/parser": "^7.29.0",
  563 + "@vue/shared": "3.5.30",
  564 + "entities": "^7.0.1",
  565 + "estree-walker": "^2.0.2",
  566 + "source-map-js": "^1.2.1"
  567 + }
  568 + },
  569 + "node_modules/@vue/compiler-dom": {
  570 + "version": "3.5.30",
  571 + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz",
  572 + "integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==",
  573 + "license": "MIT",
  574 + "dependencies": {
  575 + "@vue/compiler-core": "3.5.30",
  576 + "@vue/shared": "3.5.30"
  577 + }
  578 + },
  579 + "node_modules/@vue/compiler-sfc": {
  580 + "version": "3.5.30",
  581 + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz",
  582 + "integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==",
  583 + "license": "MIT",
  584 + "dependencies": {
  585 + "@babel/parser": "^7.29.0",
  586 + "@vue/compiler-core": "3.5.30",
  587 + "@vue/compiler-dom": "3.5.30",
  588 + "@vue/compiler-ssr": "3.5.30",
  589 + "@vue/shared": "3.5.30",
  590 + "estree-walker": "^2.0.2",
  591 + "magic-string": "^0.30.21",
  592 + "postcss": "^8.5.8",
  593 + "source-map-js": "^1.2.1"
  594 + }
  595 + },
  596 + "node_modules/@vue/compiler-ssr": {
  597 + "version": "3.5.30",
  598 + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz",
  599 + "integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==",
  600 + "license": "MIT",
  601 + "dependencies": {
  602 + "@vue/compiler-dom": "3.5.30",
  603 + "@vue/shared": "3.5.30"
  604 + }
  605 + },
  606 + "node_modules/@vue/devtools-api": {
  607 + "version": "7.7.9",
  608 + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
  609 + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
  610 + "license": "MIT",
  611 + "dependencies": {
  612 + "@vue/devtools-kit": "^7.7.9"
  613 + }
  614 + },
  615 + "node_modules/@vue/devtools-kit": {
  616 + "version": "7.7.9",
  617 + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
  618 + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
  619 + "license": "MIT",
  620 + "dependencies": {
  621 + "@vue/devtools-shared": "^7.7.9",
  622 + "birpc": "^2.3.0",
  623 + "hookable": "^5.5.3",
  624 + "mitt": "^3.0.1",
  625 + "perfect-debounce": "^1.0.0",
  626 + "speakingurl": "^14.0.1",
  627 + "superjson": "^2.2.2"
  628 + }
  629 + },
  630 + "node_modules/@vue/devtools-shared": {
  631 + "version": "7.7.9",
  632 + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
  633 + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
  634 + "license": "MIT",
  635 + "dependencies": {
  636 + "rfdc": "^1.4.1"
  637 + }
  638 + },
  639 + "node_modules/@vue/language-core": {
  640 + "version": "3.2.6",
  641 + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.6.tgz",
  642 + "integrity": "sha512-xYYYX3/aVup576tP/23sEUpgiEnujrENaoNRbaozC1/MA9I6EGFQRJb4xrt/MmUCAGlxTKL2RmT8JLTPqagCkg==",
  643 + "dev": true,
  644 + "license": "MIT",
  645 + "dependencies": {
  646 + "@volar/language-core": "2.4.28",
  647 + "@vue/compiler-dom": "^3.5.0",
  648 + "@vue/shared": "^3.5.0",
  649 + "alien-signals": "^3.0.0",
  650 + "muggle-string": "^0.4.1",
  651 + "path-browserify": "^1.0.1",
  652 + "picomatch": "^4.0.2"
  653 + }
  654 + },
  655 + "node_modules/@vue/reactivity": {
  656 + "version": "3.5.30",
  657 + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz",
  658 + "integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==",
  659 + "license": "MIT",
  660 + "dependencies": {
  661 + "@vue/shared": "3.5.30"
  662 + }
  663 + },
  664 + "node_modules/@vue/runtime-core": {
  665 + "version": "3.5.30",
  666 + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz",
  667 + "integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==",
  668 + "license": "MIT",
  669 + "dependencies": {
  670 + "@vue/reactivity": "3.5.30",
  671 + "@vue/shared": "3.5.30"
  672 + }
  673 + },
  674 + "node_modules/@vue/runtime-dom": {
  675 + "version": "3.5.30",
  676 + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz",
  677 + "integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==",
  678 + "license": "MIT",
  679 + "dependencies": {
  680 + "@vue/reactivity": "3.5.30",
  681 + "@vue/runtime-core": "3.5.30",
  682 + "@vue/shared": "3.5.30",
  683 + "csstype": "^3.2.3"
  684 + }
  685 + },
  686 + "node_modules/@vue/server-renderer": {
  687 + "version": "3.5.30",
  688 + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz",
  689 + "integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==",
  690 + "license": "MIT",
  691 + "dependencies": {
  692 + "@vue/compiler-ssr": "3.5.30",
  693 + "@vue/shared": "3.5.30"
  694 + },
  695 + "peerDependencies": {
  696 + "vue": "3.5.30"
  697 + }
  698 + },
  699 + "node_modules/@vue/shared": {
  700 + "version": "3.5.30",
  701 + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz",
  702 + "integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==",
  703 + "license": "MIT"
  704 + },
  705 + "node_modules/@vue/tsconfig": {
  706 + "version": "0.9.1",
  707 + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz",
  708 + "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==",
  709 + "dev": true,
  710 + "license": "MIT",
  711 + "peerDependencies": {
  712 + "typescript": ">= 5.8",
  713 + "vue": "^3.4.0"
  714 + },
  715 + "peerDependenciesMeta": {
  716 + "typescript": {
  717 + "optional": true
  718 + },
  719 + "vue": {
  720 + "optional": true
  721 + }
  722 + }
  723 + },
  724 + "node_modules/alien-signals": {
  725 + "version": "3.1.2",
  726 + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz",
  727 + "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==",
  728 + "dev": true,
  729 + "license": "MIT"
  730 + },
  731 + "node_modules/ant-design-vue": {
  732 + "version": "4.2.6",
  733 + "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-4.2.6.tgz",
  734 + "integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==",
  735 + "license": "MIT",
  736 + "dependencies": {
  737 + "@ant-design/colors": "^6.0.0",
  738 + "@ant-design/icons-vue": "^7.0.0",
  739 + "@babel/runtime": "^7.10.5",
  740 + "@ctrl/tinycolor": "^3.5.0",
  741 + "@emotion/hash": "^0.9.0",
  742 + "@emotion/unitless": "^0.8.0",
  743 + "@simonwep/pickr": "~1.8.0",
  744 + "array-tree-filter": "^2.1.0",
  745 + "async-validator": "^4.0.0",
  746 + "csstype": "^3.1.1",
  747 + "dayjs": "^1.10.5",
  748 + "dom-align": "^1.12.1",
  749 + "dom-scroll-into-view": "^2.0.0",
  750 + "lodash": "^4.17.21",
  751 + "lodash-es": "^4.17.15",
  752 + "resize-observer-polyfill": "^1.5.1",
  753 + "scroll-into-view-if-needed": "^2.2.25",
  754 + "shallow-equal": "^1.0.0",
  755 + "stylis": "^4.1.3",
  756 + "throttle-debounce": "^5.0.0",
  757 + "vue-types": "^3.0.0",
  758 + "warning": "^4.0.0"
  759 + },
  760 + "engines": {
  761 + "node": ">=12.22.0"
  762 + },
  763 + "funding": {
  764 + "type": "opencollective",
  765 + "url": "https://opencollective.com/ant-design-vue"
  766 + },
  767 + "peerDependencies": {
  768 + "vue": ">=3.2.0"
  769 + }
  770 + },
  771 + "node_modules/array-tree-filter": {
  772 + "version": "2.1.0",
  773 + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
  774 + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==",
  775 + "license": "MIT"
  776 + },
  777 + "node_modules/async-validator": {
  778 + "version": "4.2.5",
  779 + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
  780 + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==",
  781 + "license": "MIT"
  782 + },
  783 + "node_modules/asynckit": {
  784 + "version": "0.4.0",
  785 + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
  786 + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
  787 + "license": "MIT"
  788 + },
  789 + "node_modules/axios": {
  790 + "version": "1.13.6",
  791 + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
  792 + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==",
  793 + "license": "MIT",
  794 + "dependencies": {
  795 + "follow-redirects": "^1.15.11",
  796 + "form-data": "^4.0.5",
  797 + "proxy-from-env": "^1.1.0"
  798 + }
  799 + },
  800 + "node_modules/birpc": {
  801 + "version": "2.9.0",
  802 + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
  803 + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
  804 + "license": "MIT",
  805 + "funding": {
  806 + "url": "https://github.com/sponsors/antfu"
  807 + }
  808 + },
  809 + "node_modules/call-bind-apply-helpers": {
  810 + "version": "1.0.2",
  811 + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
  812 + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
  813 + "license": "MIT",
  814 + "dependencies": {
  815 + "es-errors": "^1.3.0",
  816 + "function-bind": "^1.1.2"
  817 + },
  818 + "engines": {
  819 + "node": ">= 0.4"
  820 + }
  821 + },
  822 + "node_modules/combined-stream": {
  823 + "version": "1.0.8",
  824 + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
  825 + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
  826 + "license": "MIT",
  827 + "dependencies": {
  828 + "delayed-stream": "~1.0.0"
  829 + },
  830 + "engines": {
  831 + "node": ">= 0.8"
  832 + }
  833 + },
  834 + "node_modules/compute-scroll-into-view": {
  835 + "version": "1.0.20",
  836 + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
  837 + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==",
  838 + "license": "MIT"
  839 + },
  840 + "node_modules/copy-anything": {
  841 + "version": "4.0.5",
  842 + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
  843 + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
  844 + "license": "MIT",
  845 + "dependencies": {
  846 + "is-what": "^5.2.0"
  847 + },
  848 + "engines": {
  849 + "node": ">=18"
  850 + },
  851 + "funding": {
  852 + "url": "https://github.com/sponsors/mesqueeb"
  853 + }
  854 + },
  855 + "node_modules/core-js": {
  856 + "version": "3.49.0",
  857 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
  858 + "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
  859 + "hasInstallScript": true,
  860 + "license": "MIT",
  861 + "funding": {
  862 + "type": "opencollective",
  863 + "url": "https://opencollective.com/core-js"
  864 + }
  865 + },
  866 + "node_modules/csstype": {
  867 + "version": "3.2.3",
  868 + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
  869 + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
  870 + "license": "MIT"
  871 + },
  872 + "node_modules/dayjs": {
  873 + "version": "1.11.20",
  874 + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz",
  875 + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==",
  876 + "license": "MIT"
  877 + },
  878 + "node_modules/delayed-stream": {
  879 + "version": "1.0.0",
  880 + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
  881 + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
  882 + "license": "MIT",
  883 + "engines": {
  884 + "node": ">=0.4.0"
  885 + }
  886 + },
  887 + "node_modules/detect-libc": {
  888 + "version": "2.1.2",
  889 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
  890 + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
  891 + "dev": true,
  892 + "license": "Apache-2.0",
  893 + "engines": {
  894 + "node": ">=8"
  895 + }
  896 + },
  897 + "node_modules/dom-align": {
  898 + "version": "1.12.4",
  899 + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz",
  900 + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==",
  901 + "license": "MIT"
  902 + },
  903 + "node_modules/dom-scroll-into-view": {
  904 + "version": "2.0.1",
  905 + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz",
  906 + "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==",
  907 + "license": "MIT"
  908 + },
  909 + "node_modules/dunder-proto": {
  910 + "version": "1.0.1",
  911 + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
  912 + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
  913 + "license": "MIT",
  914 + "dependencies": {
  915 + "call-bind-apply-helpers": "^1.0.1",
  916 + "es-errors": "^1.3.0",
  917 + "gopd": "^1.2.0"
  918 + },
  919 + "engines": {
  920 + "node": ">= 0.4"
  921 + }
  922 + },
  923 + "node_modules/entities": {
  924 + "version": "7.0.1",
  925 + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
  926 + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
  927 + "license": "BSD-2-Clause",
  928 + "engines": {
  929 + "node": ">=0.12"
  930 + },
  931 + "funding": {
  932 + "url": "https://github.com/fb55/entities?sponsor=1"
  933 + }
  934 + },
  935 + "node_modules/es-define-property": {
  936 + "version": "1.0.1",
  937 + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
  938 + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
  939 + "license": "MIT",
  940 + "engines": {
  941 + "node": ">= 0.4"
  942 + }
  943 + },
  944 + "node_modules/es-errors": {
  945 + "version": "1.3.0",
  946 + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
  947 + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
  948 + "license": "MIT",
  949 + "engines": {
  950 + "node": ">= 0.4"
  951 + }
  952 + },
  953 + "node_modules/es-object-atoms": {
  954 + "version": "1.1.1",
  955 + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
  956 + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
  957 + "license": "MIT",
  958 + "dependencies": {
  959 + "es-errors": "^1.3.0"
  960 + },
  961 + "engines": {
  962 + "node": ">= 0.4"
  963 + }
  964 + },
  965 + "node_modules/es-set-tostringtag": {
  966 + "version": "2.1.0",
  967 + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
  968 + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
  969 + "license": "MIT",
  970 + "dependencies": {
  971 + "es-errors": "^1.3.0",
  972 + "get-intrinsic": "^1.2.6",
  973 + "has-tostringtag": "^1.0.2",
  974 + "hasown": "^2.0.2"
  975 + },
  976 + "engines": {
  977 + "node": ">= 0.4"
  978 + }
  979 + },
  980 + "node_modules/estree-walker": {
  981 + "version": "2.0.2",
  982 + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
  983 + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
  984 + "license": "MIT"
  985 + },
  986 + "node_modules/fdir": {
  987 + "version": "6.5.0",
  988 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
  989 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
  990 + "dev": true,
  991 + "license": "MIT",
  992 + "engines": {
  993 + "node": ">=12.0.0"
  994 + },
  995 + "peerDependencies": {
  996 + "picomatch": "^3 || ^4"
  997 + },
  998 + "peerDependenciesMeta": {
  999 + "picomatch": {
  1000 + "optional": true
  1001 + }
  1002 + }
  1003 + },
  1004 + "node_modules/follow-redirects": {
  1005 + "version": "1.15.11",
  1006 + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
  1007 + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
  1008 + "funding": [
  1009 + {
  1010 + "type": "individual",
  1011 + "url": "https://github.com/sponsors/RubenVerborgh"
  1012 + }
  1013 + ],
  1014 + "license": "MIT",
  1015 + "engines": {
  1016 + "node": ">=4.0"
  1017 + },
  1018 + "peerDependenciesMeta": {
  1019 + "debug": {
  1020 + "optional": true
  1021 + }
  1022 + }
  1023 + },
  1024 + "node_modules/form-data": {
  1025 + "version": "4.0.5",
  1026 + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
  1027 + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
  1028 + "license": "MIT",
  1029 + "dependencies": {
  1030 + "asynckit": "^0.4.0",
  1031 + "combined-stream": "^1.0.8",
  1032 + "es-set-tostringtag": "^2.1.0",
  1033 + "hasown": "^2.0.2",
  1034 + "mime-types": "^2.1.12"
  1035 + },
  1036 + "engines": {
  1037 + "node": ">= 6"
  1038 + }
  1039 + },
  1040 + "node_modules/fsevents": {
  1041 + "version": "2.3.3",
  1042 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
  1043 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
  1044 + "dev": true,
  1045 + "hasInstallScript": true,
  1046 + "license": "MIT",
  1047 + "optional": true,
  1048 + "os": [
  1049 + "darwin"
  1050 + ],
  1051 + "engines": {
  1052 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
  1053 + }
  1054 + },
  1055 + "node_modules/function-bind": {
  1056 + "version": "1.1.2",
  1057 + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
  1058 + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
  1059 + "license": "MIT",
  1060 + "funding": {
  1061 + "url": "https://github.com/sponsors/ljharb"
  1062 + }
  1063 + },
  1064 + "node_modules/get-intrinsic": {
  1065 + "version": "1.3.0",
  1066 + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
  1067 + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
  1068 + "license": "MIT",
  1069 + "dependencies": {
  1070 + "call-bind-apply-helpers": "^1.0.2",
  1071 + "es-define-property": "^1.0.1",
  1072 + "es-errors": "^1.3.0",
  1073 + "es-object-atoms": "^1.1.1",
  1074 + "function-bind": "^1.1.2",
  1075 + "get-proto": "^1.0.1",
  1076 + "gopd": "^1.2.0",
  1077 + "has-symbols": "^1.1.0",
  1078 + "hasown": "^2.0.2",
  1079 + "math-intrinsics": "^1.1.0"
  1080 + },
  1081 + "engines": {
  1082 + "node": ">= 0.4"
  1083 + },
  1084 + "funding": {
  1085 + "url": "https://github.com/sponsors/ljharb"
  1086 + }
  1087 + },
  1088 + "node_modules/get-proto": {
  1089 + "version": "1.0.1",
  1090 + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
  1091 + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
  1092 + "license": "MIT",
  1093 + "dependencies": {
  1094 + "dunder-proto": "^1.0.1",
  1095 + "es-object-atoms": "^1.0.0"
  1096 + },
  1097 + "engines": {
  1098 + "node": ">= 0.4"
  1099 + }
  1100 + },
  1101 + "node_modules/gopd": {
  1102 + "version": "1.2.0",
  1103 + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
  1104 + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
  1105 + "license": "MIT",
  1106 + "engines": {
  1107 + "node": ">= 0.4"
  1108 + },
  1109 + "funding": {
  1110 + "url": "https://github.com/sponsors/ljharb"
  1111 + }
  1112 + },
  1113 + "node_modules/has-symbols": {
  1114 + "version": "1.1.0",
  1115 + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
  1116 + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
  1117 + "license": "MIT",
  1118 + "engines": {
  1119 + "node": ">= 0.4"
  1120 + },
  1121 + "funding": {
  1122 + "url": "https://github.com/sponsors/ljharb"
  1123 + }
  1124 + },
  1125 + "node_modules/has-tostringtag": {
  1126 + "version": "1.0.2",
  1127 + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
  1128 + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
  1129 + "license": "MIT",
  1130 + "dependencies": {
  1131 + "has-symbols": "^1.0.3"
  1132 + },
  1133 + "engines": {
  1134 + "node": ">= 0.4"
  1135 + },
  1136 + "funding": {
  1137 + "url": "https://github.com/sponsors/ljharb"
  1138 + }
  1139 + },
  1140 + "node_modules/hasown": {
  1141 + "version": "2.0.2",
  1142 + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
  1143 + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
  1144 + "license": "MIT",
  1145 + "dependencies": {
  1146 + "function-bind": "^1.1.2"
  1147 + },
  1148 + "engines": {
  1149 + "node": ">= 0.4"
  1150 + }
  1151 + },
  1152 + "node_modules/hookable": {
  1153 + "version": "5.5.3",
  1154 + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
  1155 + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
  1156 + "license": "MIT"
  1157 + },
  1158 + "node_modules/is-plain-object": {
  1159 + "version": "3.0.1",
  1160 + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
  1161 + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
  1162 + "license": "MIT",
  1163 + "engines": {
  1164 + "node": ">=0.10.0"
  1165 + }
  1166 + },
  1167 + "node_modules/is-what": {
  1168 + "version": "5.5.0",
  1169 + "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
  1170 + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
  1171 + "license": "MIT",
  1172 + "engines": {
  1173 + "node": ">=18"
  1174 + },
  1175 + "funding": {
  1176 + "url": "https://github.com/sponsors/mesqueeb"
  1177 + }
  1178 + },
  1179 + "node_modules/js-tokens": {
  1180 + "version": "4.0.0",
  1181 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
  1182 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
  1183 + "license": "MIT"
  1184 + },
  1185 + "node_modules/lightningcss": {
  1186 + "version": "1.32.0",
  1187 + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
  1188 + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
  1189 + "dev": true,
  1190 + "license": "MPL-2.0",
  1191 + "dependencies": {
  1192 + "detect-libc": "^2.0.3"
  1193 + },
  1194 + "engines": {
  1195 + "node": ">= 12.0.0"
  1196 + },
  1197 + "funding": {
  1198 + "type": "opencollective",
  1199 + "url": "https://opencollective.com/parcel"
  1200 + },
  1201 + "optionalDependencies": {
  1202 + "lightningcss-android-arm64": "1.32.0",
  1203 + "lightningcss-darwin-arm64": "1.32.0",
  1204 + "lightningcss-darwin-x64": "1.32.0",
  1205 + "lightningcss-freebsd-x64": "1.32.0",
  1206 + "lightningcss-linux-arm-gnueabihf": "1.32.0",
  1207 + "lightningcss-linux-arm64-gnu": "1.32.0",
  1208 + "lightningcss-linux-arm64-musl": "1.32.0",
  1209 + "lightningcss-linux-x64-gnu": "1.32.0",
  1210 + "lightningcss-linux-x64-musl": "1.32.0",
  1211 + "lightningcss-win32-arm64-msvc": "1.32.0",
  1212 + "lightningcss-win32-x64-msvc": "1.32.0"
  1213 + }
  1214 + },
  1215 + "node_modules/lightningcss-android-arm64": {
  1216 + "version": "1.32.0",
  1217 + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
  1218 + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
  1219 + "cpu": [
  1220 + "arm64"
  1221 + ],
  1222 + "dev": true,
  1223 + "license": "MPL-2.0",
  1224 + "optional": true,
  1225 + "os": [
  1226 + "android"
  1227 + ],
  1228 + "engines": {
  1229 + "node": ">= 12.0.0"
  1230 + },
  1231 + "funding": {
  1232 + "type": "opencollective",
  1233 + "url": "https://opencollective.com/parcel"
  1234 + }
  1235 + },
  1236 + "node_modules/lightningcss-darwin-arm64": {
  1237 + "version": "1.32.0",
  1238 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
  1239 + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
  1240 + "cpu": [
  1241 + "arm64"
  1242 + ],
  1243 + "dev": true,
  1244 + "license": "MPL-2.0",
  1245 + "optional": true,
  1246 + "os": [
  1247 + "darwin"
  1248 + ],
  1249 + "engines": {
  1250 + "node": ">= 12.0.0"
  1251 + },
  1252 + "funding": {
  1253 + "type": "opencollective",
  1254 + "url": "https://opencollective.com/parcel"
  1255 + }
  1256 + },
  1257 + "node_modules/lightningcss-darwin-x64": {
  1258 + "version": "1.32.0",
  1259 + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
  1260 + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
  1261 + "cpu": [
  1262 + "x64"
  1263 + ],
  1264 + "dev": true,
  1265 + "license": "MPL-2.0",
  1266 + "optional": true,
  1267 + "os": [
  1268 + "darwin"
  1269 + ],
  1270 + "engines": {
  1271 + "node": ">= 12.0.0"
  1272 + },
  1273 + "funding": {
  1274 + "type": "opencollective",
  1275 + "url": "https://opencollective.com/parcel"
  1276 + }
  1277 + },
  1278 + "node_modules/lightningcss-freebsd-x64": {
  1279 + "version": "1.32.0",
  1280 + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
  1281 + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
  1282 + "cpu": [
  1283 + "x64"
  1284 + ],
  1285 + "dev": true,
  1286 + "license": "MPL-2.0",
  1287 + "optional": true,
  1288 + "os": [
  1289 + "freebsd"
  1290 + ],
  1291 + "engines": {
  1292 + "node": ">= 12.0.0"
  1293 + },
  1294 + "funding": {
  1295 + "type": "opencollective",
  1296 + "url": "https://opencollective.com/parcel"
  1297 + }
  1298 + },
  1299 + "node_modules/lightningcss-linux-arm-gnueabihf": {
  1300 + "version": "1.32.0",
  1301 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
  1302 + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
  1303 + "cpu": [
  1304 + "arm"
  1305 + ],
  1306 + "dev": true,
  1307 + "license": "MPL-2.0",
  1308 + "optional": true,
  1309 + "os": [
  1310 + "linux"
  1311 + ],
  1312 + "engines": {
  1313 + "node": ">= 12.0.0"
  1314 + },
  1315 + "funding": {
  1316 + "type": "opencollective",
  1317 + "url": "https://opencollective.com/parcel"
  1318 + }
  1319 + },
  1320 + "node_modules/lightningcss-linux-arm64-gnu": {
  1321 + "version": "1.32.0",
  1322 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
  1323 + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
  1324 + "cpu": [
  1325 + "arm64"
  1326 + ],
  1327 + "dev": true,
  1328 + "libc": [
  1329 + "glibc"
  1330 + ],
  1331 + "license": "MPL-2.0",
  1332 + "optional": true,
  1333 + "os": [
  1334 + "linux"
  1335 + ],
  1336 + "engines": {
  1337 + "node": ">= 12.0.0"
  1338 + },
  1339 + "funding": {
  1340 + "type": "opencollective",
  1341 + "url": "https://opencollective.com/parcel"
  1342 + }
  1343 + },
  1344 + "node_modules/lightningcss-linux-arm64-musl": {
  1345 + "version": "1.32.0",
  1346 + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
  1347 + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
  1348 + "cpu": [
  1349 + "arm64"
  1350 + ],
  1351 + "dev": true,
  1352 + "libc": [
  1353 + "musl"
  1354 + ],
  1355 + "license": "MPL-2.0",
  1356 + "optional": true,
  1357 + "os": [
  1358 + "linux"
  1359 + ],
  1360 + "engines": {
  1361 + "node": ">= 12.0.0"
  1362 + },
  1363 + "funding": {
  1364 + "type": "opencollective",
  1365 + "url": "https://opencollective.com/parcel"
  1366 + }
  1367 + },
  1368 + "node_modules/lightningcss-linux-x64-gnu": {
  1369 + "version": "1.32.0",
  1370 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
  1371 + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
  1372 + "cpu": [
  1373 + "x64"
  1374 + ],
  1375 + "dev": true,
  1376 + "libc": [
  1377 + "glibc"
  1378 + ],
  1379 + "license": "MPL-2.0",
  1380 + "optional": true,
  1381 + "os": [
  1382 + "linux"
  1383 + ],
  1384 + "engines": {
  1385 + "node": ">= 12.0.0"
  1386 + },
  1387 + "funding": {
  1388 + "type": "opencollective",
  1389 + "url": "https://opencollective.com/parcel"
  1390 + }
  1391 + },
  1392 + "node_modules/lightningcss-linux-x64-musl": {
  1393 + "version": "1.32.0",
  1394 + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
  1395 + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
  1396 + "cpu": [
  1397 + "x64"
  1398 + ],
  1399 + "dev": true,
  1400 + "libc": [
  1401 + "musl"
  1402 + ],
  1403 + "license": "MPL-2.0",
  1404 + "optional": true,
  1405 + "os": [
  1406 + "linux"
  1407 + ],
  1408 + "engines": {
  1409 + "node": ">= 12.0.0"
  1410 + },
  1411 + "funding": {
  1412 + "type": "opencollective",
  1413 + "url": "https://opencollective.com/parcel"
  1414 + }
  1415 + },
  1416 + "node_modules/lightningcss-win32-arm64-msvc": {
  1417 + "version": "1.32.0",
  1418 + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
  1419 + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
  1420 + "cpu": [
  1421 + "arm64"
  1422 + ],
  1423 + "dev": true,
  1424 + "license": "MPL-2.0",
  1425 + "optional": true,
  1426 + "os": [
  1427 + "win32"
  1428 + ],
  1429 + "engines": {
  1430 + "node": ">= 12.0.0"
  1431 + },
  1432 + "funding": {
  1433 + "type": "opencollective",
  1434 + "url": "https://opencollective.com/parcel"
  1435 + }
  1436 + },
  1437 + "node_modules/lightningcss-win32-x64-msvc": {
  1438 + "version": "1.32.0",
  1439 + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
  1440 + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
  1441 + "cpu": [
  1442 + "x64"
  1443 + ],
  1444 + "dev": true,
  1445 + "license": "MPL-2.0",
  1446 + "optional": true,
  1447 + "os": [
  1448 + "win32"
  1449 + ],
  1450 + "engines": {
  1451 + "node": ">= 12.0.0"
  1452 + },
  1453 + "funding": {
  1454 + "type": "opencollective",
  1455 + "url": "https://opencollective.com/parcel"
  1456 + }
  1457 + },
  1458 + "node_modules/lodash": {
  1459 + "version": "4.17.23",
  1460 + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
  1461 + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
  1462 + "license": "MIT"
  1463 + },
  1464 + "node_modules/lodash-es": {
  1465 + "version": "4.17.23",
  1466 + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
  1467 + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
  1468 + "license": "MIT"
  1469 + },
  1470 + "node_modules/loose-envify": {
  1471 + "version": "1.4.0",
  1472 + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
  1473 + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
  1474 + "license": "MIT",
  1475 + "dependencies": {
  1476 + "js-tokens": "^3.0.0 || ^4.0.0"
  1477 + },
  1478 + "bin": {
  1479 + "loose-envify": "cli.js"
  1480 + }
  1481 + },
  1482 + "node_modules/magic-string": {
  1483 + "version": "0.30.21",
  1484 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
  1485 + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
  1486 + "license": "MIT",
  1487 + "dependencies": {
  1488 + "@jridgewell/sourcemap-codec": "^1.5.5"
  1489 + }
  1490 + },
  1491 + "node_modules/math-intrinsics": {
  1492 + "version": "1.1.0",
  1493 + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
  1494 + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
  1495 + "license": "MIT",
  1496 + "engines": {
  1497 + "node": ">= 0.4"
  1498 + }
  1499 + },
  1500 + "node_modules/mime-db": {
  1501 + "version": "1.52.0",
  1502 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
  1503 + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
  1504 + "license": "MIT",
  1505 + "engines": {
  1506 + "node": ">= 0.6"
  1507 + }
  1508 + },
  1509 + "node_modules/mime-types": {
  1510 + "version": "2.1.35",
  1511 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
  1512 + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
  1513 + "license": "MIT",
  1514 + "dependencies": {
  1515 + "mime-db": "1.52.0"
  1516 + },
  1517 + "engines": {
  1518 + "node": ">= 0.6"
  1519 + }
  1520 + },
  1521 + "node_modules/mitt": {
  1522 + "version": "3.0.1",
  1523 + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
  1524 + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
  1525 + "license": "MIT"
  1526 + },
  1527 + "node_modules/muggle-string": {
  1528 + "version": "0.4.1",
  1529 + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
  1530 + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
  1531 + "dev": true,
  1532 + "license": "MIT"
  1533 + },
  1534 + "node_modules/nanoid": {
  1535 + "version": "3.3.11",
  1536 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
  1537 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
  1538 + "funding": [
  1539 + {
  1540 + "type": "github",
  1541 + "url": "https://github.com/sponsors/ai"
  1542 + }
  1543 + ],
  1544 + "license": "MIT",
  1545 + "bin": {
  1546 + "nanoid": "bin/nanoid.cjs"
  1547 + },
  1548 + "engines": {
  1549 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
  1550 + }
  1551 + },
  1552 + "node_modules/nanopop": {
  1553 + "version": "2.4.2",
  1554 + "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.4.2.tgz",
  1555 + "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==",
  1556 + "license": "MIT"
  1557 + },
  1558 + "node_modules/path-browserify": {
  1559 + "version": "1.0.1",
  1560 + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
  1561 + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
  1562 + "dev": true,
  1563 + "license": "MIT"
  1564 + },
  1565 + "node_modules/perfect-debounce": {
  1566 + "version": "1.0.0",
  1567 + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
  1568 + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
  1569 + "license": "MIT"
  1570 + },
  1571 + "node_modules/picocolors": {
  1572 + "version": "1.1.1",
  1573 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
  1574 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
  1575 + "license": "ISC"
  1576 + },
  1577 + "node_modules/picomatch": {
  1578 + "version": "4.0.4",
  1579 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
  1580 + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
  1581 + "dev": true,
  1582 + "license": "MIT",
  1583 + "engines": {
  1584 + "node": ">=12"
  1585 + },
  1586 + "funding": {
  1587 + "url": "https://github.com/sponsors/jonschlinkert"
  1588 + }
  1589 + },
  1590 + "node_modules/pinia": {
  1591 + "version": "3.0.4",
  1592 + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
  1593 + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
  1594 + "license": "MIT",
  1595 + "dependencies": {
  1596 + "@vue/devtools-api": "^7.7.7"
  1597 + },
  1598 + "funding": {
  1599 + "url": "https://github.com/sponsors/posva"
  1600 + },
  1601 + "peerDependencies": {
  1602 + "typescript": ">=4.5.0",
  1603 + "vue": "^3.5.11"
  1604 + },
  1605 + "peerDependenciesMeta": {
  1606 + "typescript": {
  1607 + "optional": true
  1608 + }
  1609 + }
  1610 + },
  1611 + "node_modules/postcss": {
  1612 + "version": "8.5.8",
  1613 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
  1614 + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
  1615 + "funding": [
  1616 + {
  1617 + "type": "opencollective",
  1618 + "url": "https://opencollective.com/postcss/"
  1619 + },
  1620 + {
  1621 + "type": "tidelift",
  1622 + "url": "https://tidelift.com/funding/github/npm/postcss"
  1623 + },
  1624 + {
  1625 + "type": "github",
  1626 + "url": "https://github.com/sponsors/ai"
  1627 + }
  1628 + ],
  1629 + "license": "MIT",
  1630 + "dependencies": {
  1631 + "nanoid": "^3.3.11",
  1632 + "picocolors": "^1.1.1",
  1633 + "source-map-js": "^1.2.1"
  1634 + },
  1635 + "engines": {
  1636 + "node": "^10 || ^12 || >=14"
  1637 + }
  1638 + },
  1639 + "node_modules/proxy-from-env": {
  1640 + "version": "1.1.0",
  1641 + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
  1642 + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
  1643 + "license": "MIT"
  1644 + },
  1645 + "node_modules/resize-observer-polyfill": {
  1646 + "version": "1.5.1",
  1647 + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
  1648 + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
  1649 + "license": "MIT"
  1650 + },
  1651 + "node_modules/rfdc": {
  1652 + "version": "1.4.1",
  1653 + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
  1654 + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
  1655 + "license": "MIT"
  1656 + },
  1657 + "node_modules/rolldown": {
  1658 + "version": "1.0.0-rc.11",
  1659 + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.11.tgz",
  1660 + "integrity": "sha512-NRjoKMusSjfRbSYiH3VSumlkgFe7kYAa3pzVOsVYVFY3zb5d7nS+a3KGQ7hJKXuYWbzJKPVQ9Wxq2UvyK+ENpw==",
  1661 + "dev": true,
  1662 + "license": "MIT",
  1663 + "dependencies": {
  1664 + "@oxc-project/types": "=0.122.0",
  1665 + "@rolldown/pluginutils": "1.0.0-rc.11"
  1666 + },
  1667 + "bin": {
  1668 + "rolldown": "bin/cli.mjs"
  1669 + },
  1670 + "engines": {
  1671 + "node": "^20.19.0 || >=22.12.0"
  1672 + },
  1673 + "optionalDependencies": {
  1674 + "@rolldown/binding-android-arm64": "1.0.0-rc.11",
  1675 + "@rolldown/binding-darwin-arm64": "1.0.0-rc.11",
  1676 + "@rolldown/binding-darwin-x64": "1.0.0-rc.11",
  1677 + "@rolldown/binding-freebsd-x64": "1.0.0-rc.11",
  1678 + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.11",
  1679 + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.11",
  1680 + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.11",
  1681 + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.11",
  1682 + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.11",
  1683 + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.11",
  1684 + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.11",
  1685 + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.11",
  1686 + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.11",
  1687 + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.11",
  1688 + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.11"
  1689 + }
  1690 + },
  1691 + "node_modules/rolldown/node_modules/@rolldown/pluginutils": {
  1692 + "version": "1.0.0-rc.11",
  1693 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.11.tgz",
  1694 + "integrity": "sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==",
  1695 + "dev": true,
  1696 + "license": "MIT"
  1697 + },
  1698 + "node_modules/scroll-into-view-if-needed": {
  1699 + "version": "2.2.31",
  1700 + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
  1701 + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
  1702 + "license": "MIT",
  1703 + "dependencies": {
  1704 + "compute-scroll-into-view": "^1.0.20"
  1705 + }
  1706 + },
  1707 + "node_modules/shallow-equal": {
  1708 + "version": "1.2.1",
  1709 + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
  1710 + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==",
  1711 + "license": "MIT"
  1712 + },
  1713 + "node_modules/source-map-js": {
  1714 + "version": "1.2.1",
  1715 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
  1716 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
  1717 + "license": "BSD-3-Clause",
  1718 + "engines": {
  1719 + "node": ">=0.10.0"
  1720 + }
  1721 + },
  1722 + "node_modules/speakingurl": {
  1723 + "version": "14.0.1",
  1724 + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
  1725 + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
  1726 + "license": "BSD-3-Clause",
  1727 + "engines": {
  1728 + "node": ">=0.10.0"
  1729 + }
  1730 + },
  1731 + "node_modules/stylis": {
  1732 + "version": "4.3.6",
  1733 + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
  1734 + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
  1735 + "license": "MIT"
  1736 + },
  1737 + "node_modules/superjson": {
  1738 + "version": "2.2.6",
  1739 + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
  1740 + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
  1741 + "license": "MIT",
  1742 + "dependencies": {
  1743 + "copy-anything": "^4"
  1744 + },
  1745 + "engines": {
  1746 + "node": ">=16"
  1747 + }
  1748 + },
  1749 + "node_modules/throttle-debounce": {
  1750 + "version": "5.0.2",
  1751 + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
  1752 + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
  1753 + "license": "MIT",
  1754 + "engines": {
  1755 + "node": ">=12.22"
  1756 + }
  1757 + },
  1758 + "node_modules/tinyglobby": {
  1759 + "version": "0.2.15",
  1760 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
  1761 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
  1762 + "dev": true,
  1763 + "license": "MIT",
  1764 + "dependencies": {
  1765 + "fdir": "^6.5.0",
  1766 + "picomatch": "^4.0.3"
  1767 + },
  1768 + "engines": {
  1769 + "node": ">=12.0.0"
  1770 + },
  1771 + "funding": {
  1772 + "url": "https://github.com/sponsors/SuperchupuDev"
  1773 + }
  1774 + },
  1775 + "node_modules/tslib": {
  1776 + "version": "2.8.1",
  1777 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
  1778 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
  1779 + "dev": true,
  1780 + "license": "0BSD",
  1781 + "optional": true
  1782 + },
  1783 + "node_modules/typescript": {
  1784 + "version": "5.9.3",
  1785 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
  1786 + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
  1787 + "devOptional": true,
  1788 + "license": "Apache-2.0",
  1789 + "bin": {
  1790 + "tsc": "bin/tsc",
  1791 + "tsserver": "bin/tsserver"
  1792 + },
  1793 + "engines": {
  1794 + "node": ">=14.17"
  1795 + }
  1796 + },
  1797 + "node_modules/undici-types": {
  1798 + "version": "7.16.0",
  1799 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
  1800 + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
  1801 + "dev": true,
  1802 + "license": "MIT"
  1803 + },
  1804 + "node_modules/vite": {
  1805 + "version": "8.0.2",
  1806 + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.2.tgz",
  1807 + "integrity": "sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==",
  1808 + "dev": true,
  1809 + "license": "MIT",
  1810 + "dependencies": {
  1811 + "lightningcss": "^1.32.0",
  1812 + "picomatch": "^4.0.3",
  1813 + "postcss": "^8.5.8",
  1814 + "rolldown": "1.0.0-rc.11",
  1815 + "tinyglobby": "^0.2.15"
  1816 + },
  1817 + "bin": {
  1818 + "vite": "bin/vite.js"
  1819 + },
  1820 + "engines": {
  1821 + "node": "^20.19.0 || >=22.12.0"
  1822 + },
  1823 + "funding": {
  1824 + "url": "https://github.com/vitejs/vite?sponsor=1"
  1825 + },
  1826 + "optionalDependencies": {
  1827 + "fsevents": "~2.3.3"
  1828 + },
  1829 + "peerDependencies": {
  1830 + "@types/node": "^20.19.0 || >=22.12.0",
  1831 + "@vitejs/devtools": "^0.1.0",
  1832 + "esbuild": "^0.27.0",
  1833 + "jiti": ">=1.21.0",
  1834 + "less": "^4.0.0",
  1835 + "sass": "^1.70.0",
  1836 + "sass-embedded": "^1.70.0",
  1837 + "stylus": ">=0.54.8",
  1838 + "sugarss": "^5.0.0",
  1839 + "terser": "^5.16.0",
  1840 + "tsx": "^4.8.1",
  1841 + "yaml": "^2.4.2"
  1842 + },
  1843 + "peerDependenciesMeta": {
  1844 + "@types/node": {
  1845 + "optional": true
  1846 + },
  1847 + "@vitejs/devtools": {
  1848 + "optional": true
  1849 + },
  1850 + "esbuild": {
  1851 + "optional": true
  1852 + },
  1853 + "jiti": {
  1854 + "optional": true
  1855 + },
  1856 + "less": {
  1857 + "optional": true
  1858 + },
  1859 + "sass": {
  1860 + "optional": true
  1861 + },
  1862 + "sass-embedded": {
  1863 + "optional": true
  1864 + },
  1865 + "stylus": {
  1866 + "optional": true
  1867 + },
  1868 + "sugarss": {
  1869 + "optional": true
  1870 + },
  1871 + "terser": {
  1872 + "optional": true
  1873 + },
  1874 + "tsx": {
  1875 + "optional": true
  1876 + },
  1877 + "yaml": {
  1878 + "optional": true
  1879 + }
  1880 + }
  1881 + },
  1882 + "node_modules/vscode-uri": {
  1883 + "version": "3.1.0",
  1884 + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
  1885 + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
  1886 + "dev": true,
  1887 + "license": "MIT"
  1888 + },
  1889 + "node_modules/vue": {
  1890 + "version": "3.5.30",
  1891 + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz",
  1892 + "integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==",
  1893 + "license": "MIT",
  1894 + "dependencies": {
  1895 + "@vue/compiler-dom": "3.5.30",
  1896 + "@vue/compiler-sfc": "3.5.30",
  1897 + "@vue/runtime-dom": "3.5.30",
  1898 + "@vue/server-renderer": "3.5.30",
  1899 + "@vue/shared": "3.5.30"
  1900 + },
  1901 + "peerDependencies": {
  1902 + "typescript": "*"
  1903 + },
  1904 + "peerDependenciesMeta": {
  1905 + "typescript": {
  1906 + "optional": true
  1907 + }
  1908 + }
  1909 + },
  1910 + "node_modules/vue-router": {
  1911 + "version": "4.6.4",
  1912 + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
  1913 + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
  1914 + "license": "MIT",
  1915 + "dependencies": {
  1916 + "@vue/devtools-api": "^6.6.4"
  1917 + },
  1918 + "funding": {
  1919 + "url": "https://github.com/sponsors/posva"
  1920 + },
  1921 + "peerDependencies": {
  1922 + "vue": "^3.5.0"
  1923 + }
  1924 + },
  1925 + "node_modules/vue-router/node_modules/@vue/devtools-api": {
  1926 + "version": "6.6.4",
  1927 + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
  1928 + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
  1929 + "license": "MIT"
  1930 + },
  1931 + "node_modules/vue-tsc": {
  1932 + "version": "3.2.6",
  1933 + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.6.tgz",
  1934 + "integrity": "sha512-gYW/kWI0XrwGzd0PKc7tVB/qpdeAkIZLNZb10/InizkQjHjnT8weZ/vBarZoj4kHKbUTZT/bAVgoOr8x4NsQ/Q==",
  1935 + "dev": true,
  1936 + "license": "MIT",
  1937 + "dependencies": {
  1938 + "@volar/typescript": "2.4.28",
  1939 + "@vue/language-core": "3.2.6"
  1940 + },
  1941 + "bin": {
  1942 + "vue-tsc": "bin/vue-tsc.js"
  1943 + },
  1944 + "peerDependencies": {
  1945 + "typescript": ">=5.0.0"
  1946 + }
  1947 + },
  1948 + "node_modules/vue-types": {
  1949 + "version": "3.0.2",
  1950 + "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz",
  1951 + "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==",
  1952 + "license": "MIT",
  1953 + "dependencies": {
  1954 + "is-plain-object": "3.0.1"
  1955 + },
  1956 + "engines": {
  1957 + "node": ">=10.15.0"
  1958 + },
  1959 + "peerDependencies": {
  1960 + "vue": "^3.0.0"
  1961 + }
  1962 + },
  1963 + "node_modules/warning": {
  1964 + "version": "4.0.3",
  1965 + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
  1966 + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
  1967 + "license": "MIT",
  1968 + "dependencies": {
  1969 + "loose-envify": "^1.0.0"
  1970 + }
  1971 + }
  1972 + }
  1973 +}
... ...
package.json 0 → 100644
  1 +++ a/package.json
  1 +{
  2 + "name": "rider-admin",
  3 + "private": true,
  4 + "version": "0.0.0",
  5 + "type": "module",
  6 + "scripts": {
  7 + "dev": "vite --host",
  8 + "build": "vue-tsc -b && vite build",
  9 + "preview": "vite preview"
  10 + },
  11 + "dependencies": {
  12 + "@ant-design/icons-vue": "^7.0.1",
  13 + "ant-design-vue": "^4.2.6",
  14 + "axios": "^1.13.6",
  15 + "dayjs": "^1.11.20",
  16 + "pinia": "^3.0.4",
  17 + "vue": "^3.5.30",
  18 + "vue-router": "^4.6.4"
  19 + },
  20 + "devDependencies": {
  21 + "@types/node": "^24.12.0",
  22 + "@vitejs/plugin-vue": "^6.0.5",
  23 + "@vue/tsconfig": "^0.9.0",
  24 + "typescript": "~5.9.3",
  25 + "vite": "^8.0.1",
  26 + "vue-tsc": "^3.2.5"
  27 + }
  28 +}
0 29 \ No newline at end of file
... ...
public/favicon.svg 0 → 100644
  1 +++ a/public/favicon.svg
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
0 2 \ No newline at end of file
... ...
public/icons.svg 0 → 100644
  1 +++ a/public/icons.svg
  1 +<svg xmlns="http://www.w3.org/2000/svg">
  2 + <symbol id="bluesky-icon" viewBox="0 0 16 17">
  3 + <g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
  4 + <defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
  5 + </symbol>
  6 + <symbol id="discord-icon" viewBox="0 0 20 19">
  7 + <path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
  8 + </symbol>
  9 + <symbol id="documentation-icon" viewBox="0 0 21 20">
  10 + <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
  11 + <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
  12 + <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
  13 + </symbol>
  14 + <symbol id="github-icon" viewBox="0 0 19 19">
  15 + <path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
  16 + </symbol>
  17 + <symbol id="social-icon" viewBox="0 0 20 20">
  18 + <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
  19 + <path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
  20 + </symbol>
  21 + <symbol id="x-icon" viewBox="0 0 19 19">
  22 + <path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
  23 + </symbol>
  24 +</svg>
... ...
src/App.vue 0 → 100644
  1 +++ a/src/App.vue
  1 +<template>
  2 + <router-view />
  3 +</template>
... ...
src/api/index.ts 0 → 100644
  1 +++ a/src/api/index.ts
  1 +import request from '@/utils/request'
  2 +
  3 +// 城市管理
  4 +export const cityApi = {
  5 + tree: () => request.get('/api/platform/city/tree'),
  6 + openList: () => request.get('/api/platform/city/open'),
  7 + add: (data: any) => request.post('/api/platform/city/add', data),
  8 + edit: (data: any) => request.put('/api/platform/city/edit', data),
  9 + setStatus: (cityId: number, status: number) =>
  10 + request.post('/api/platform/city/setStatus', null, { params: { cityId, status } }),
  11 + getConfig: (cityId: number) => request.get(`/api/platform/city/config/${cityId}`),
  12 + updateConfig: (cityId: number, config: any) =>
  13 + request.post(`/api/platform/city/config/${cityId}`, config),
  14 +}
  15 +
  16 +// 分站管理
  17 +export const substationApi = {
  18 + list: (keyword?: string) => request.get('/api/platform/substation/list', { params: { keyword } }),
  19 + add: (data: any) => request.post('/api/platform/substation/add', data),
  20 + edit: (data: any) => request.put('/api/platform/substation/edit', data),
  21 + ban: (id: number) => request.post('/api/platform/substation/ban', null, { params: { id } }),
  22 + cancelBan: (id: number) => request.post('/api/platform/substation/cancelBan', null, { params: { id } }),
  23 + del: (id: number) => request.delete('/api/platform/substation/del', { params: { id } }),
  24 + changePassword: (data: { oldPassword: string; newPassword: string }) =>
  25 + request.post('/api/platform/substation/changePassword', data),
  26 +}
  27 +
  28 +// 商家管理
  29 +export const merchantApi = {
  30 + enterList: (params: any) => request.get('/api/platform/merchant/enter/list', { params }),
  31 + handleEnter: (id: number, status: number) =>
  32 + request.post('/api/platform/merchant/enter/handle', null, { params: { id, status } }),
  33 + storeList: (params: any) => request.get('/api/platform/merchant/store/list', { params }),
  34 + addStore: (data: any) => request.post('/api/platform/merchant/store/add', data),
  35 + editStore: (data: any) => request.put('/api/platform/merchant/store/edit', data),
  36 + storeDetail: (storeId: number) => request.get('/api/platform/merchant/store/detail', { params: { storeId } }),
  37 + setOperatingState: (storeId: number, state: number) =>
  38 + request.post('/api/platform/merchant/store/setOperatingState', null, { params: { storeId, state } }),
  39 + setAutoOrder: (storeId: number, auto: number) =>
  40 + request.post('/api/platform/merchant/store/setAutoOrder', null, { params: { storeId, auto } }),
  41 + updateFeeConfig: (storeId: number, freeShipping: number, upToSend: number) =>
  42 + request.post('/api/platform/merchant/store/updateFeeConfig', null,
  43 + { params: { storeId, freeShipping, upToSend } }),
  44 + delStore: (storeId: number) => request.delete('/api/platform/merchant/store/del', { params: { storeId } }),
  45 +}
  46 +
  47 +// 骑手管理
  48 +export const riderApi = {
  49 + add: (data: any) => request.post('/api/admin/rider/add', data),
  50 + list: (params: any) => request.get('/api/admin/rider/list', { params }),
  51 + setStatus: (riderId: number, status: number) =>
  52 + request.post('/api/admin/rider/setStatus', null, { params: { riderId, status } }),
  53 + setLevel: (riderId: number, levelId?: number) =>
  54 + request.post('/api/admin/rider/setLevel', null, { params: { riderId, levelId } }),
  55 + setEnableStatus: (riderId: number, status: number) =>
  56 + request.post('/api/admin/rider/setEnableStatus', null, { params: { riderId, status } }),
  57 + setType: (riderId: number, type: number) =>
  58 + request.post('/api/admin/rider/setType', null, { params: { riderId, type } }),
  59 + designate: (orderId: number, riderId: number) =>
  60 + request.post('/api/admin/rider/order/designate', null, { params: { orderId, riderId } }),
  61 + setTrans: (orderId: number, trans: number) =>
  62 + request.post('/api/admin/rider/order/setTrans', null, { params: { orderId, trans } }),
  63 + evaluateList: (riderId: number, type = 0, page = 1) =>
  64 + request.get('/api/admin/evaluate/list', { params: { riderId, type, page } }),
  65 + // 订单列表(分站管理员/超管)
  66 + orderList: (params: any) => request.get('/api/admin/rider/order/list', { params }),
  67 + // 配送订单列表(外部系统推入的)
  68 + deliveryOrderList: (params: any) => request.get('/api/admin/rider/order/delivery/list', { params }),
  69 +}
  70 +
  71 +export const riderLevelApi = {
  72 + list: (cityId: number) => request.get('/api/admin/rider/level/list', { params: { cityId } }),
  73 + add: (data: any) => request.post('/api/admin/rider/level/add', data),
  74 + edit: (data: any) => request.put('/api/admin/rider/level/edit', data),
  75 + setDefault: (id: number, cityId: number) =>
  76 + request.post('/api/admin/rider/level/setDefault', null, { params: { id, cityId } }),
  77 + del: (id: number, cityId: number) =>
  78 + request.delete('/api/admin/rider/level/del', { params: { id, cityId } }),
  79 +}
  80 +
  81 +// 退款管理
  82 +export const refundApi = {
  83 + reasons: (role = 0) => request.get('/api/admin/refund/reasons', { params: { role } }),
  84 + record: (orderId: number) => request.get('/api/admin/refund/record', { params: { orderId } }),
  85 + handle: (recordId: number, status: number, remark = '') =>
  86 + request.post('/api/admin/refund/handle', null, { params: { recordId, status, remark } }),
  87 +}
  88 +
  89 +// 开放平台
  90 +export const openApi = {
  91 + list: (page = 1) => request.get('/api/platform/open/list', { params: { page } }),
  92 + create: (data: any) => request.post('/api/platform/open/create', null, { params: data }),
  93 + resetSecret: (appId: number) =>
  94 + request.post('/api/platform/open/resetSecret', null, { params: { appId } }),
  95 + setStatus: (appId: number, status: number) =>
  96 + request.post('/api/platform/open/setStatus', null, { params: { appId, status } }),
  97 + updateWebhook: (appId: number, webhookUrl: string, webhookEvents: string) =>
  98 + request.post('/api/platform/open/updateWebhook', null,
  99 + { params: { appId, webhookUrl, webhookEvents } }),
  100 + webhookLogs: (appId: number, page = 1) =>
  101 + request.get('/api/platform/open/webhook/logs', { params: { appId, page } }),
  102 + retryWebhook: (logId: number) =>
  103 + request.post('/api/platform/open/webhook/retry', null, { params: { logId } }),
  104 +}
  105 +
  106 +// 配送费计算
  107 +export const deliveryApi = {
  108 + calc: (data: any) => request.post('/api/delivery/fee/calc', data),
  109 + check: (cityId: number, orderType: number) =>
  110 + request.get('/api/delivery/fee/check', { params: { cityId, orderType } }),
  111 +}
  112 +
  113 +// 配送订单管理(外部系统推入的订单)
  114 +export const deliveryOrderApi = {
  115 + create: (data: any) => request.post('/api/open/delivery/order/create', data),
  116 + query: (outOrderNo: string) =>
  117 + request.get('/api/open/delivery/order/query', { params: { outOrderNo } }),
  118 + cancel: (outOrderNo: string) =>
  119 + request.post('/api/open/delivery/order/cancel', null, { params: { outOrderNo } }),
  120 +}
  121 +
  122 +// 附近骑手
  123 +export const locationApi = {
  124 + nearby: (cityId: number, lng: string, lat: string) =>
  125 + request.get('/api/rider/location/nearby', { params: { cityId, lng, lat } }),
  126 +}
... ...
src/assets/hero.png 0 → 100644

43.9 KB

src/assets/vite.svg 0 → 100644
  1 +++ a/src/assets/vite.svg
  1 +<svg xmlns="http://www.w3.org/2000/svg" width="77" height="47" fill="none" aria-labelledby="vite-logo-title" viewBox="0 0 77 47"><title id="vite-logo-title">Vite</title><style>.parenthesis{fill:#000}@media (prefers-color-scheme:dark){.parenthesis{fill:#fff}}</style><path fill="#9135ff" d="M40.151 45.71c-.663.844-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.493c-.92 0-1.457-1.04-.92-1.788l7.479-10.471c1.07-1.498 0-3.578-1.842-3.578H15.443c-.92 0-1.456-1.04-.92-1.788l9.696-13.576c.213-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.472c-1.07 1.497 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.087.89 1.83L40.153 45.712z"/><mask id="a" width="48" height="47" x="14" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M40.047 45.71c-.663.843-2.02.374-2.02-.699V34.708a2.26 2.26 0 0 0-2.262-2.262H24.389c-.92 0-1.457-1.04-.92-1.788l7.479-10.472c1.07-1.497 0-3.578-1.842-3.578H15.34c-.92 0-1.456-1.04-.92-1.788l9.696-13.575c.213-.297.556-.474.92-.474H53.93c.92 0 1.456 1.04.92 1.788L47.37 13.03c-1.07 1.498 0 3.578 1.842 3.578h11.376c.944 0 1.474 1.088.89 1.831L40.049 45.712z"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#eee6ff" rx="5.508" ry="14.704" transform="rotate(269.814 20.96 11.29)scale(-1 1)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#eee6ff" rx="10.399" ry="29.851" transform="rotate(89.814 -16.902 -8.275)scale(1 -1)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#8900ff" rx="5.508" ry="30.487" transform="rotate(89.814 -19.197 -7.127)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.928 4.177)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#8900ff" rx="5.508" ry="30.599" transform="rotate(89.814 -25.738 5.52)scale(1 -1)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#eee6ff" rx="14.072" ry="22.078" transform="rotate(93.35 31.245 55.578)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#8900ff" rx="3.47" ry="21.501" transform="rotate(89.009 35.419 55.202)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx="14.592" cy="9.743" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(39.51 14.592 9.743)"/></g><g filter="url(#k)"><ellipse cx="61.728" cy="-5.321" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 61.728 -5.32)"/></g><g filter="url(#l)"><ellipse cx="55.618" cy="7.104" fill="#00c2ff" rx="5.971" ry="9.665" transform="rotate(37.892 55.618 7.104)"/></g><g filter="url(#m)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#n)"><ellipse cx="12.326" cy="39.103" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 12.326 39.103)"/></g><g filter="url(#o)"><ellipse cx="49.857" cy="30.678" fill="#8900ff" rx="4.407" ry="29.108" transform="rotate(37.892 49.857 30.678)"/></g><g filter="url(#p)"><ellipse cx="52.623" cy="33.171" fill="#00c2ff" rx="5.971" ry="15.297" transform="rotate(37.892 52.623 33.17)"/></g></g><path d="M6.919 0c-9.198 13.166-9.252 33.575 0 46.789h6.215c-9.25-13.214-9.196-33.623 0-46.789zm62.424 0h-6.215c9.198 13.166 9.252 33.575 0 46.789h6.215c9.25-13.214 9.196-33.623 0-46.789" class="parenthesis"/><defs><filter id="b" width="60.045" height="41.654" x="-5.564" y="16.92" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-40.407" y="-6.762" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-35.435" y="2.801" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-30.84" y="20.8" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-29.307" y="21.949" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="29.961" y="-17.13" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="37.754" y="3.055" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="37.754" y="3.055" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-13.43" y="-22.082" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="34.321" y="-37.644" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="38.847" y="-10.552" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-15.081" y="6.78" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-15.081" y="6.78" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="22.45" y="-1.645" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="32.919" y="11.36" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17286" stdDeviation="4.596"/></filter></defs></svg>
... ...
src/assets/vue.svg 0 → 100644
  1 +++ a/src/assets/vue.svg
  1 +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
0 2 \ No newline at end of file
... ...
src/components/HelloWorld.vue 0 → 100644
  1 +++ a/src/components/HelloWorld.vue
  1 +<script setup lang="ts">
  2 +import { ref } from 'vue'
  3 +import viteLogo from '../assets/vite.svg'
  4 +import heroImg from '../assets/hero.png'
  5 +import vueLogo from '../assets/vue.svg'
  6 +
  7 +const count = ref(0)
  8 +</script>
  9 +
  10 +<template>
  11 + <section id="center">
  12 + <div class="hero">
  13 + <img :src="heroImg" class="base" width="170" height="179" alt="" />
  14 + <img :src="vueLogo" class="framework" alt="Vue logo" />
  15 + <img :src="viteLogo" class="vite" alt="Vite logo" />
  16 + </div>
  17 + <div>
  18 + <h1>Get started</h1>
  19 + <p>Edit <code>src/App.vue</code> and save to test <code>HMR</code></p>
  20 + </div>
  21 + <button class="counter" @click="count++">Count is {{ count }}</button>
  22 + </section>
  23 +
  24 + <div class="ticks"></div>
  25 +
  26 + <section id="next-steps">
  27 + <div id="docs">
  28 + <svg class="icon" role="presentation" aria-hidden="true">
  29 + <use href="/icons.svg#documentation-icon"></use>
  30 + </svg>
  31 + <h2>Documentation</h2>
  32 + <p>Your questions, answered</p>
  33 + <ul>
  34 + <li>
  35 + <a href="https://vite.dev/" target="_blank">
  36 + <img class="logo" :src="viteLogo" alt="" />
  37 + Explore Vite
  38 + </a>
  39 + </li>
  40 + <li>
  41 + <a href="https://vuejs.org/" target="_blank">
  42 + <img class="button-icon" :src="vueLogo" alt="" />
  43 + Learn more
  44 + </a>
  45 + </li>
  46 + </ul>
  47 + </div>
  48 + <div id="social">
  49 + <svg class="icon" role="presentation" aria-hidden="true">
  50 + <use href="/icons.svg#social-icon"></use>
  51 + </svg>
  52 + <h2>Connect with us</h2>
  53 + <p>Join the Vite community</p>
  54 + <ul>
  55 + <li>
  56 + <a href="https://github.com/vitejs/vite" target="_blank">
  57 + <svg class="button-icon" role="presentation" aria-hidden="true">
  58 + <use href="/icons.svg#github-icon"></use>
  59 + </svg>
  60 + GitHub
  61 + </a>
  62 + </li>
  63 + <li>
  64 + <a href="https://chat.vite.dev/" target="_blank">
  65 + <svg class="button-icon" role="presentation" aria-hidden="true">
  66 + <use href="/icons.svg#discord-icon"></use>
  67 + </svg>
  68 + Discord
  69 + </a>
  70 + </li>
  71 + <li>
  72 + <a href="https://x.com/vite_js" target="_blank">
  73 + <svg class="button-icon" role="presentation" aria-hidden="true">
  74 + <use href="/icons.svg#x-icon"></use>
  75 + </svg>
  76 + X.com
  77 + </a>
  78 + </li>
  79 + <li>
  80 + <a href="https://bsky.app/profile/vite.dev" target="_blank">
  81 + <svg class="button-icon" role="presentation" aria-hidden="true">
  82 + <use href="/icons.svg#bluesky-icon"></use>
  83 + </svg>
  84 + Bluesky
  85 + </a>
  86 + </li>
  87 + </ul>
  88 + </div>
  89 + </section>
  90 +
  91 + <div class="ticks"></div>
  92 + <section id="spacer"></section>
  93 +</template>
... ...
src/layouts/MainLayout.vue 0 → 100644
  1 +++ a/src/layouts/MainLayout.vue
  1 +<template>
  2 + <a-layout style="min-height: 100vh">
  3 + <a-layout-sider v-model:collapsed="collapsed" collapsible>
  4 + <div class="logo">{{ collapsed ? '地利' : '地利外卖管理' }}</div>
  5 + <a-menu
  6 + v-model:selectedKeys="selectedKeys"
  7 + theme="dark"
  8 + mode="inline"
  9 + @click="onMenuClick"
  10 + >
  11 + <a-menu-item key="/city">
  12 + <template #icon><global-outlined /></template>
  13 + 城市管理
  14 + </a-menu-item>
  15 + <a-menu-item key="/substation">
  16 + <template #icon><apartment-outlined /></template>
  17 + 分站管理
  18 + </a-menu-item>
  19 + <a-sub-menu key="merchant">
  20 + <template #icon><shop-outlined /></template>
  21 + <template #title>商家管理</template>
  22 + <a-menu-item key="/merchant/enter">入驻申请</a-menu-item>
  23 + <a-menu-item key="/merchant/store">店铺管理</a-menu-item>
  24 + </a-sub-menu>
  25 + <a-menu-item key="/rider">
  26 + <template #icon><user-outlined /></template>
  27 + 骑手管理
  28 + </a-menu-item>
  29 + <a-menu-item key="/rider/evaluate">
  30 + <template #icon><star-outlined /></template>
  31 + 骑手评价
  32 + </a-menu-item>
  33 + <a-sub-menu key="orders">
  34 + <template #icon><unordered-list-outlined /></template>
  35 + <template #title>订单管理</template>
  36 + <a-menu-item key="/order">订单列表</a-menu-item>
  37 + <a-menu-item key="/refund">退款管理</a-menu-item>
  38 + <a-menu-item key="/delivery/order">配送订单</a-menu-item>
  39 + </a-sub-menu>
  40 + <a-menu-item key="/open">
  41 + <template #icon><api-outlined /></template>
  42 + 开放平台
  43 + </a-menu-item>
  44 + </a-menu>
  45 + </a-layout-sider>
  46 + <a-layout>
  47 + <a-layout-header style="background:#fff;padding:0 16px;display:flex;align-items:center;justify-content:flex-end">
  48 + <a-dropdown>
  49 + <a-button type="text">管理员 <down-outlined /></a-button>
  50 + <template #overlay>
  51 + <a-menu>
  52 + <a-menu-item @click="handleLogout">退出登录</a-menu-item>
  53 + </a-menu>
  54 + </template>
  55 + </a-dropdown>
  56 + </a-layout-header>
  57 + <a-layout-content style="margin:16px">
  58 + <router-view />
  59 + </a-layout-content>
  60 + </a-layout>
  61 + </a-layout>
  62 +</template>
  63 +
  64 +<script setup lang="ts">
  65 +import { ref, watch } from 'vue'
  66 +import { useRouter, useRoute } from 'vue-router'
  67 +import { useAuthStore } from '@/stores/auth'
  68 +import {
  69 + GlobalOutlined, ApartmentOutlined, ShopOutlined,
  70 + UserOutlined, UnorderedListOutlined, ApiOutlined, DownOutlined, StarOutlined
  71 +} from '@ant-design/icons-vue'
  72 +
  73 +const router = useRouter()
  74 +const route = useRoute()
  75 +const auth = useAuthStore()
  76 +const collapsed = ref(false)
  77 +const selectedKeys = ref([route.path])
  78 +
  79 +watch(() => route.path, (p) => { selectedKeys.value = [p] })
  80 +
  81 +function onMenuClick({ key }: { key: string }) {
  82 + router.push(key)
  83 +}
  84 +
  85 +function handleLogout() {
  86 + auth.logout()
  87 + router.push('/login')
  88 +}
  89 +</script>
  90 +
  91 +<style scoped>
  92 +.logo {
  93 + height: 64px;
  94 + color: #fff;
  95 + font-size: 16px;
  96 + font-weight: bold;
  97 + display: flex;
  98 + align-items: center;
  99 + justify-content: center;
  100 + overflow: hidden;
  101 + white-space: nowrap;
  102 +}
  103 +</style>
... ...
src/main.ts 0 → 100644
  1 +++ a/src/main.ts
  1 +import { createApp } from 'vue'
  2 +import { createPinia } from 'pinia'
  3 +import Antd from 'ant-design-vue'
  4 +import 'ant-design-vue/dist/reset.css'
  5 +import App from './App.vue'
  6 +import router from './router'
  7 +
  8 +const app = createApp(App)
  9 +app.use(createPinia())
  10 +app.use(router)
  11 +app.use(Antd)
  12 +app.mount('#app')
... ...
src/router/index.ts 0 → 100644
  1 +++ a/src/router/index.ts
  1 +import { createRouter, createWebHistory } from 'vue-router'
  2 +import { useAuthStore } from '@/stores/auth'
  3 +
  4 +const router = createRouter({
  5 + history: createWebHistory(),
  6 + routes: [
  7 + {
  8 + path: '/login',
  9 + name: 'Login',
  10 + component: () => import('@/views/Login.vue'),
  11 + meta: { public: true },
  12 + },
  13 + {
  14 + path: '/',
  15 + component: () => import('@/layouts/MainLayout.vue'),
  16 + redirect: '/city',
  17 + children: [
  18 + {
  19 + path: 'city',
  20 + name: 'City',
  21 + component: () => import('@/views/city/CityList.vue'),
  22 + meta: { title: '城市管理' },
  23 + },
  24 + {
  25 + path: 'substation',
  26 + name: 'Substation',
  27 + component: () => import('@/views/substation/SubstationList.vue'),
  28 + meta: { title: '分站管理' },
  29 + },
  30 + {
  31 + path: 'merchant/enter',
  32 + name: 'MerchantEnter',
  33 + component: () => import('@/views/merchant/EnterList.vue'),
  34 + meta: { title: '入驻申请' },
  35 + },
  36 + {
  37 + path: 'merchant/store',
  38 + name: 'MerchantStore',
  39 + component: () => import('@/views/merchant/StoreList.vue'),
  40 + meta: { title: '店铺管理' },
  41 + },
  42 + {
  43 + path: 'rider',
  44 + name: 'Rider',
  45 + component: () => import('@/views/rider/RiderList.vue'),
  46 + meta: { title: '骑手管理' },
  47 + },
  48 + {
  49 + path: 'order',
  50 + name: 'Order',
  51 + component: () => import('@/views/order/OrderList.vue'),
  52 + meta: { title: '订单管理' },
  53 + },
  54 + {
  55 + path: 'refund',
  56 + name: 'Refund',
  57 + component: () => import('@/views/order/RefundList.vue'),
  58 + meta: { title: '退款管理' },
  59 + },
  60 + {
  61 + path: 'delivery/order',
  62 + name: 'DeliveryOrder',
  63 + component: () => import('@/views/delivery/DeliveryOrderList.vue'),
  64 + meta: { title: '配送订单' },
  65 + },
  66 + {
  67 + path: 'rider/evaluate',
  68 + name: 'RiderEvaluate',
  69 + component: () => import('@/views/rider/RiderEvaluateList.vue'),
  70 + meta: { title: '骑手评价' },
  71 + },
  72 + {
  73 + path: 'open',
  74 + name: 'OpenApp',
  75 + component: () => import('@/views/open/OpenAppList.vue'),
  76 + meta: { title: '开放平台' },
  77 + },
  78 + ],
  79 + },
  80 + { path: '/:pathMatch(.*)*', redirect: '/' },
  81 + ],
  82 +})
  83 +
  84 +router.beforeEach((to) => {
  85 + const auth = useAuthStore()
  86 + if (!to.meta.public && !auth.token) {
  87 + return { path: '/login' }
  88 + }
  89 +})
  90 +
  91 +export default router
... ...
src/stores/auth.ts 0 → 100644
  1 +++ a/src/stores/auth.ts
  1 +import { defineStore } from 'pinia'
  2 +import { ref } from 'vue'
  3 +
  4 +export const useAuthStore = defineStore('auth', () => {
  5 + const token = ref(localStorage.getItem('token') || '')
  6 + const userInfo = ref<any>(JSON.parse(localStorage.getItem('userInfo') || 'null'))
  7 +
  8 + function setToken(t: string) {
  9 + token.value = t
  10 + localStorage.setItem('token', t)
  11 + }
  12 +
  13 + function setUserInfo(info: any) {
  14 + userInfo.value = info
  15 + localStorage.setItem('userInfo', JSON.stringify(info))
  16 + }
  17 +
  18 + function logout() {
  19 + token.value = ''
  20 + userInfo.value = null
  21 + localStorage.removeItem('token')
  22 + localStorage.removeItem('userInfo')
  23 + }
  24 +
  25 + return { token, userInfo, setToken, setUserInfo, logout }
  26 +})
... ...
src/style.css 0 → 100644
  1 +++ a/src/style.css
  1 +:root {
  2 + --text: #6b6375;
  3 + --text-h: #08060d;
  4 + --bg: #fff;
  5 + --border: #e5e4e7;
  6 + --code-bg: #f4f3ec;
  7 + --accent: #aa3bff;
  8 + --accent-bg: rgba(170, 59, 255, 0.1);
  9 + --accent-border: rgba(170, 59, 255, 0.5);
  10 + --social-bg: rgba(244, 243, 236, 0.5);
  11 + --shadow:
  12 + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
  13 +
  14 + --sans: system-ui, 'Segoe UI', Roboto, sans-serif;
  15 + --heading: system-ui, 'Segoe UI', Roboto, sans-serif;
  16 + --mono: ui-monospace, Consolas, monospace;
  17 +
  18 + font: 18px/145% var(--sans);
  19 + letter-spacing: 0.18px;
  20 + color-scheme: light dark;
  21 + color: var(--text);
  22 + background: var(--bg);
  23 + font-synthesis: none;
  24 + text-rendering: optimizeLegibility;
  25 + -webkit-font-smoothing: antialiased;
  26 + -moz-osx-font-smoothing: grayscale;
  27 +
  28 + @media (max-width: 1024px) {
  29 + font-size: 16px;
  30 + }
  31 +}
  32 +
  33 +@media (prefers-color-scheme: dark) {
  34 + :root {
  35 + --text: #9ca3af;
  36 + --text-h: #f3f4f6;
  37 + --bg: #16171d;
  38 + --border: #2e303a;
  39 + --code-bg: #1f2028;
  40 + --accent: #c084fc;
  41 + --accent-bg: rgba(192, 132, 252, 0.15);
  42 + --accent-border: rgba(192, 132, 252, 0.5);
  43 + --social-bg: rgba(47, 48, 58, 0.5);
  44 + --shadow:
  45 + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
  46 + }
  47 +
  48 + #social .button-icon {
  49 + filter: invert(1) brightness(2);
  50 + }
  51 +}
  52 +
  53 +body {
  54 + margin: 0;
  55 +}
  56 +
  57 +h1,
  58 +h2 {
  59 + font-family: var(--heading);
  60 + font-weight: 500;
  61 + color: var(--text-h);
  62 +}
  63 +
  64 +h1 {
  65 + font-size: 56px;
  66 + letter-spacing: -1.68px;
  67 + margin: 32px 0;
  68 + @media (max-width: 1024px) {
  69 + font-size: 36px;
  70 + margin: 20px 0;
  71 + }
  72 +}
  73 +h2 {
  74 + font-size: 24px;
  75 + line-height: 118%;
  76 + letter-spacing: -0.24px;
  77 + margin: 0 0 8px;
  78 + @media (max-width: 1024px) {
  79 + font-size: 20px;
  80 + }
  81 +}
  82 +p {
  83 + margin: 0;
  84 +}
  85 +
  86 +code,
  87 +.counter {
  88 + font-family: var(--mono);
  89 + display: inline-flex;
  90 + border-radius: 4px;
  91 + color: var(--text-h);
  92 +}
  93 +
  94 +code {
  95 + font-size: 15px;
  96 + line-height: 135%;
  97 + padding: 4px 8px;
  98 + background: var(--code-bg);
  99 +}
  100 +
  101 +.counter {
  102 + font-size: 16px;
  103 + padding: 5px 10px;
  104 + border-radius: 5px;
  105 + color: var(--accent);
  106 + background: var(--accent-bg);
  107 + border: 2px solid transparent;
  108 + transition: border-color 0.3s;
  109 + margin-bottom: 24px;
  110 +
  111 + &:hover {
  112 + border-color: var(--accent-border);
  113 + }
  114 + &:focus-visible {
  115 + outline: 2px solid var(--accent);
  116 + outline-offset: 2px;
  117 + }
  118 +}
  119 +
  120 +.hero {
  121 + position: relative;
  122 +
  123 + .base,
  124 + .framework,
  125 + .vite {
  126 + inset-inline: 0;
  127 + margin: 0 auto;
  128 + }
  129 +
  130 + .base {
  131 + width: 170px;
  132 + position: relative;
  133 + z-index: 0;
  134 + }
  135 +
  136 + .framework,
  137 + .vite {
  138 + position: absolute;
  139 + }
  140 +
  141 + .framework {
  142 + z-index: 1;
  143 + top: 34px;
  144 + height: 28px;
  145 + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
  146 + scale(1.4);
  147 + }
  148 +
  149 + .vite {
  150 + z-index: 0;
  151 + top: 107px;
  152 + height: 26px;
  153 + width: auto;
  154 + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
  155 + scale(0.8);
  156 + }
  157 +}
  158 +
  159 +#app {
  160 + width: 1126px;
  161 + max-width: 100%;
  162 + margin: 0 auto;
  163 + text-align: center;
  164 + border-inline: 1px solid var(--border);
  165 + min-height: 100svh;
  166 + display: flex;
  167 + flex-direction: column;
  168 + box-sizing: border-box;
  169 +}
  170 +
  171 +#center {
  172 + display: flex;
  173 + flex-direction: column;
  174 + gap: 25px;
  175 + place-content: center;
  176 + place-items: center;
  177 + flex-grow: 1;
  178 +
  179 + @media (max-width: 1024px) {
  180 + padding: 32px 20px 24px;
  181 + gap: 18px;
  182 + }
  183 +}
  184 +
  185 +#next-steps {
  186 + display: flex;
  187 + border-top: 1px solid var(--border);
  188 + text-align: left;
  189 +
  190 + & > div {
  191 + flex: 1 1 0;
  192 + padding: 32px;
  193 + @media (max-width: 1024px) {
  194 + padding: 24px 20px;
  195 + }
  196 + }
  197 +
  198 + .icon {
  199 + margin-bottom: 16px;
  200 + width: 22px;
  201 + height: 22px;
  202 + }
  203 +
  204 + @media (max-width: 1024px) {
  205 + flex-direction: column;
  206 + text-align: center;
  207 + }
  208 +}
  209 +
  210 +#docs {
  211 + border-right: 1px solid var(--border);
  212 +
  213 + @media (max-width: 1024px) {
  214 + border-right: none;
  215 + border-bottom: 1px solid var(--border);
  216 + }
  217 +}
  218 +
  219 +#next-steps ul {
  220 + list-style: none;
  221 + padding: 0;
  222 + display: flex;
  223 + gap: 8px;
  224 + margin: 32px 0 0;
  225 +
  226 + .logo {
  227 + height: 18px;
  228 + }
  229 +
  230 + a {
  231 + color: var(--text-h);
  232 + font-size: 16px;
  233 + border-radius: 6px;
  234 + background: var(--social-bg);
  235 + display: flex;
  236 + padding: 6px 12px;
  237 + align-items: center;
  238 + gap: 8px;
  239 + text-decoration: none;
  240 + transition: box-shadow 0.3s;
  241 +
  242 + &:hover {
  243 + box-shadow: var(--shadow);
  244 + }
  245 + .button-icon {
  246 + height: 18px;
  247 + width: 18px;
  248 + }
  249 + }
  250 +
  251 + @media (max-width: 1024px) {
  252 + margin-top: 20px;
  253 + flex-wrap: wrap;
  254 + justify-content: center;
  255 +
  256 + li {
  257 + flex: 1 1 calc(50% - 8px);
  258 + }
  259 +
  260 + a {
  261 + width: 100%;
  262 + justify-content: center;
  263 + box-sizing: border-box;
  264 + }
  265 + }
  266 +}
  267 +
  268 +#spacer {
  269 + height: 88px;
  270 + border-top: 1px solid var(--border);
  271 + @media (max-width: 1024px) {
  272 + height: 48px;
  273 + }
  274 +}
  275 +
  276 +.ticks {
  277 + position: relative;
  278 + width: 100%;
  279 +
  280 + &::before,
  281 + &::after {
  282 + content: '';
  283 + position: absolute;
  284 + top: -4.5px;
  285 + border: 5px solid transparent;
  286 + }
  287 +
  288 + &::before {
  289 + left: 0;
  290 + border-left-color: var(--border);
  291 + }
  292 + &::after {
  293 + right: 0;
  294 + border-right-color: var(--border);
  295 + }
  296 +}
... ...
src/utils/request.ts 0 → 100644
  1 +++ a/src/utils/request.ts
  1 +import axios from 'axios'
  2 +import { message } from 'ant-design-vue'
  3 +import { useAuthStore } from '@/stores/auth'
  4 +
  5 +const request = axios.create({
  6 + baseURL: import.meta.env.VITE_API_BASE_URL || 'http://10.30.110.149:8080',
  7 + timeout: 10000,
  8 +})
  9 +
  10 +// 请求拦截:自动携带 token
  11 +request.interceptors.request.use((config) => {
  12 + const auth = useAuthStore()
  13 + if (auth.token) {
  14 + config.headers.Authorization = `Bearer ${auth.token}`
  15 + }
  16 + return config
  17 +})
  18 +
  19 +// 响应拦截:统一处理错误
  20 +request.interceptors.response.use(
  21 + (res) => {
  22 + const data = res.data
  23 + if (data.code !== 0) {
  24 + message.error(data.msg || '操作失败')
  25 + return Promise.reject(new Error(data.msg))
  26 + }
  27 + return data
  28 + },
  29 + (err) => {
  30 + if (err.response?.status === 401) {
  31 + const auth = useAuthStore()
  32 + auth.logout()
  33 + window.location.href = '/login'
  34 + } else {
  35 + message.error(err.message || '网络错误')
  36 + }
  37 + return Promise.reject(err)
  38 + }
  39 +)
  40 +
  41 +export default request
... ...
src/views/Login.vue 0 → 100644
  1 +++ a/src/views/Login.vue
  1 +<template>
  2 + <div class="login-wrap">
  3 + <a-card title="外卖管理系统" style="width:400px">
  4 + <a-form :model="form" @finish="onSubmit" layout="vertical">
  5 + <a-form-item name="role" label="登录身份">
  6 + <a-radio-group v-model:value="form.role" button-style="solid">
  7 + <a-radio-button value="substation">分站管理员</a-radio-button>
  8 + <a-radio-button value="admin">超级管理员</a-radio-button>
  9 + </a-radio-group>
  10 + </a-form-item>
  11 + <a-form-item name="account" :rules="[{ required: true, message: '请输入账号' }]">
  12 + <a-input v-model:value="form.account" placeholder="登录账号" size="large">
  13 + <template #prefix><user-outlined /></template>
  14 + </a-input>
  15 + </a-form-item>
  16 + <a-form-item name="pass" :rules="[{ required: true, message: '请输入密码' }]">
  17 + <a-input-password v-model:value="form.pass" placeholder="密码" size="large">
  18 + <template #prefix><lock-outlined /></template>
  19 + </a-input-password>
  20 + </a-form-item>
  21 + <a-form-item>
  22 + <a-button type="primary" html-type="submit" block size="large" :loading="loading">
  23 + 登录
  24 + </a-button>
  25 + </a-form-item>
  26 + </a-form>
  27 + </a-card>
  28 + </div>
  29 +</template>
  30 +
  31 +<script setup lang="ts">
  32 +import { reactive, ref } from 'vue'
  33 +import { useRouter } from 'vue-router'
  34 +import { message } from 'ant-design-vue'
  35 +import { UserOutlined, LockOutlined } from '@ant-design/icons-vue'
  36 +import { useAuthStore } from '@/stores/auth'
  37 +import request from '@/utils/request'
  38 +
  39 +const router = useRouter()
  40 +const auth = useAuthStore()
  41 +const loading = ref(false)
  42 +const form = reactive({ account: '', pass: '', role: 'substation' })
  43 +
  44 +async function onSubmit() {
  45 + loading.value = true
  46 + try {
  47 + const res: any = await request.post('/api/admin/auth/login', form)
  48 + auth.setToken(res.data.token)
  49 + auth.setUserInfo(res.data)
  50 + router.push('/')
  51 + } catch {
  52 + // 错误已由 request 拦截器处理
  53 + } finally {
  54 + loading.value = false
  55 + }
  56 +}
  57 +</script>
  58 +
  59 +<style scoped>
  60 +.login-wrap {
  61 + min-height: 100vh;
  62 + display: flex;
  63 + align-items: center;
  64 + justify-content: center;
  65 + background: #f0f2f5;
  66 +}
  67 +</style>
... ...
src/views/city/CityList.vue 0 → 100644
  1 +++ a/src/views/city/CityList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="城市/租户管理" :bordered="false">
  4 + <template #extra>
  5 + <a-button type="primary" @click="openAdd">新增</a-button>
  6 + </template>
  7 + <a-table :dataSource="list" :columns="columns" :loading="loading"
  8 + rowKey="id" :pagination="false">
  9 + <template #bodyCell="{ column, record }">
  10 + <template v-if="column.key === 'status'">
  11 + <a-tag :color="record.status === 1 ? 'green' : 'default'">
  12 + {{ record.status === 1 ? '已开通' : '未开通' }}
  13 + </a-tag>
  14 + </template>
  15 + <template v-if="column.key === 'action'">
  16 + <a-space>
  17 + <a @click="openEdit(record)">编辑</a>
  18 + <a @click="openConfig(record)">配送费配置</a>
  19 + <a @click="openLevelConfig(record)">骑手等级</a>
  20 + <a-popconfirm
  21 + :title="record.status === 1 ? '确认关闭?' : '确认开通?'"
  22 + @confirm="toggleStatus(record)">
  23 + <a>{{ record.status === 1 ? '关闭' : '开通' }}</a>
  24 + </a-popconfirm>
  25 + </a-space>
  26 + </template>
  27 + </template>
  28 + </a-table>
  29 + </a-card>
  30 +
  31 + <!-- 新增/编辑弹窗 -->
  32 + <a-modal v-model:open="modalVisible" :title="editingId ? '编辑' : '新增城市/租户'"
  33 + @ok="handleSave" :confirmLoading="saving">
  34 + <a-form :model="form" layout="vertical">
  35 + <a-form-item label="名称">
  36 + <a-input v-model:value="form.name" placeholder="如:广州市 / 某租户名" />
  37 + </a-form-item>
  38 + <a-form-item label="区划码/编号(选填)">
  39 + <a-input v-model:value="form.areaCode" placeholder="行政区划码或自定义编号" />
  40 + </a-form-item>
  41 + <a-form-item label="平台抽成比例(%)">
  42 + <a-input-number v-model:value="form.rate" :min="0" :max="100" style="width:100%" />
  43 + </a-form-item>
  44 + <a-form-item label="排序">
  45 + <a-input-number v-model:value="form.listOrder" :min="0" style="width:100%" />
  46 + </a-form-item>
  47 + </a-form>
  48 + </a-modal>
  49 +
  50 + <!-- 配送费配置弹窗 -->
  51 + <a-modal v-model:open="configVisible" title="配送费配置" width="760px"
  52 + @ok="handleConfigSave" :confirmLoading="saving">
  53 + <a-form :model="config" layout="vertical" v-if="config">
  54 + <a-form-item label="计费模式">
  55 + <a-radio-group v-model:value="config.type6.feeMode">
  56 + <a-radio :value="1">固定费用</a-radio>
  57 + <a-radio :value="2">按距离/重量计费</a-radio>
  58 + </a-radio-group>
  59 + </a-form-item>
  60 +
  61 + <template v-if="config.type6.feeMode === 1">
  62 + <a-form-item label="固定费用(元)">
  63 + <a-input-number v-model:value="config.type6.fixMoney" :min="0" style="width:100%" />
  64 + </a-form-item>
  65 + </template>
  66 +
  67 + <template v-else>
  68 + <a-divider orientation="left">距离计费</a-divider>
  69 + <a-form-item label="启用距离计费">
  70 + <a-radio-group v-model:value="config.type6.distanceSwitch">
  71 + <a-radio :value="1">启用</a-radio>
  72 + <a-radio :value="0">关闭</a-radio>
  73 + </a-radio-group>
  74 + </a-form-item>
  75 + <template v-if="config.type6.distanceSwitch === 1">
  76 + <a-row :gutter="16">
  77 + <a-col :span="12">
  78 + <a-form-item label="基础距离(km)">
  79 + <a-input-number v-model:value="config.type6.distanceBasic" :min="0" :step="0.1" style="width:100%" />
  80 + </a-form-item>
  81 + </a-col>
  82 + <a-col :span="12">
  83 + <a-form-item label="基础距离内费用(元)">
  84 + <a-input-number v-model:value="config.type6.distanceBasicMoney" :min="0" :step="0.1" style="width:100%" />
  85 + </a-form-item>
  86 + </a-col>
  87 + </a-row>
  88 + <a-row :gutter="16">
  89 + <a-col :span="12">
  90 + <a-form-item label="超出每km费用(元)">
  91 + <a-input-number v-model:value="config.type6.distanceMoreMoney" :min="0" :step="0.1" style="width:100%" />
  92 + </a-form-item>
  93 + </a-col>
  94 + <a-col :span="12">
  95 + <a-form-item label="距离取整方式">
  96 + <a-select v-model:value="config.type6.distanceType">
  97 + <a-select-option :value="1">四舍五入</a-select-option>
  98 + <a-select-option :value="2">向上取整</a-select-option>
  99 + <a-select-option :value="3">向下取整</a-select-option>
  100 + </a-select>
  101 + </a-form-item>
  102 + </a-col>
  103 + </a-row>
  104 + </template>
  105 +
  106 + <a-divider orientation="left">重量计费</a-divider>
  107 + <a-form-item label="启用重量计费">
  108 + <a-radio-group v-model:value="config.type6.weightSwitch">
  109 + <a-radio :value="1">启用</a-radio>
  110 + <a-radio :value="0">关闭</a-radio>
  111 + </a-radio-group>
  112 + </a-form-item>
  113 + <template v-if="config.type6.weightSwitch === 1">
  114 + <a-row :gutter="16">
  115 + <a-col :span="12">
  116 + <a-form-item label="基础重量(kg)">
  117 + <a-input-number v-model:value="config.type6.weightBasic" :min="0" :step="0.1" style="width:100%" />
  118 + </a-form-item>
  119 + </a-col>
  120 + <a-col :span="12">
  121 + <a-form-item label="基础重量内费用(元)">
  122 + <a-input-number v-model:value="config.type6.weightBasicMoney" :min="0" :step="0.1" style="width:100%" />
  123 + </a-form-item>
  124 + </a-col>
  125 + </a-row>
  126 + <a-row :gutter="16">
  127 + <a-col :span="12">
  128 + <a-form-item label="超出每kg费用(元)">
  129 + <a-input-number v-model:value="config.type6.weightMoreMoney" :min="0" :step="0.1" style="width:100%" />
  130 + </a-form-item>
  131 + </a-col>
  132 + <a-col :span="12">
  133 + <a-form-item label="重量取整方式">
  134 + <a-select v-model:value="config.type6.weightType">
  135 + <a-select-option :value="1">四舍五入</a-select-option>
  136 + <a-select-option :value="2">向上取整</a-select-option>
  137 + <a-select-option :value="3">向下取整</a-select-option>
  138 + </a-select>
  139 + </a-form-item>
  140 + </a-col>
  141 + </a-row>
  142 + </template>
  143 +
  144 + <a-divider orientation="left">时段附加费</a-divider>
  145 + <div style="margin-bottom:12px">
  146 + <a-button type="dashed" block @click="addTimePeriod">新增时段</a-button>
  147 + </div>
  148 + <div v-if="timePeriods.length">
  149 + <a-row
  150 + v-for="(period, index) in timePeriods"
  151 + :key="index"
  152 + :gutter="12"
  153 + style="margin-bottom:12px;align-items:flex-start"
  154 + >
  155 + <a-col :span="5">
  156 + <a-form-item :label="index === 0 ? '开始时间' : ''">
  157 + <a-input v-model:value="period.startText" placeholder="08:00" />
  158 + </a-form-item>
  159 + </a-col>
  160 + <a-col :span="5">
  161 + <a-form-item :label="index === 0 ? '结束时间' : ''">
  162 + <a-input v-model:value="period.endText" placeholder="22:00" />
  163 + </a-form-item>
  164 + </a-col>
  165 + <a-col :span="5">
  166 + <a-form-item :label="index === 0 ? '附加费(元)' : ''">
  167 + <a-input-number v-model:value="period.money" :min="0" :step="0.1" style="width:100%" />
  168 + </a-form-item>
  169 + </a-col>
  170 + <a-col :span="5">
  171 + <a-form-item :label="index === 0 ? '状态' : ''">
  172 + <a-select v-model:value="period.isOpen">
  173 + <a-select-option :value="1">启用</a-select-option>
  174 + <a-select-option :value="0">关闭</a-select-option>
  175 + </a-select>
  176 + </a-form-item>
  177 + </a-col>
  178 + <a-col :span="4">
  179 + <a-form-item :label="index === 0 ? '操作' : ''">
  180 + <a-button danger block @click="removeTimePeriod(index)">删除</a-button>
  181 + </a-form-item>
  182 + </a-col>
  183 + </a-row>
  184 + </div>
  185 + <a-empty v-else description="暂无时段附加费配置" />
  186 + </template>
  187 +
  188 + <a-divider orientation="left">预计送达与展示</a-divider>
  189 + <a-row :gutter="16">
  190 + <a-col :span="12">
  191 + <a-form-item label="预计送达基础时间(分钟)">
  192 + <a-input-number v-model:value="config.distanceBasicTime" :min="0" style="width:100%" />
  193 + </a-form-item>
  194 + </a-col>
  195 + <a-col :span="12">
  196 + <a-form-item label="超出每km增加时间(分钟)">
  197 + <a-input-number v-model:value="config.distanceMoreTime" :min="0" style="width:100%" />
  198 + </a-form-item>
  199 + </a-col>
  200 + </a-row>
  201 + <a-form-item label="附近骑手显示范围(km)">
  202 + <a-input-number v-model:value="config.riderDistance" :min="0" :step="0.1" style="width:100%" />
  203 + </a-form-item>
  204 + </a-form>
  205 + </a-modal>
  206 +
  207 + <a-modal v-model:open="levelVisible" :title="`骑手等级配置 - ${levelCityName}`" width="900px" :footer="null">
  208 + <div style="margin-bottom:16px;text-align:right">
  209 + <a-button type="primary" @click="openLevelAdd">新增等级</a-button>
  210 + </div>
  211 + <a-table :dataSource="levelList" :columns="levelColumns" :loading="levelLoading" rowKey="id" :pagination="false">
  212 + <template #bodyCell="{ column, record }">
  213 + <template v-if="column.key === 'isDefault'">
  214 + <a-tag :color="record.isDefault === 1 ? 'green' : 'default'">
  215 + {{ record.isDefault === 1 ? '默认' : '普通' }}
  216 + </a-tag>
  217 + </template>
  218 + <template v-if="column.key === 'runFeeMode'">
  219 + {{ runFeeModeMap[record.runFeeMode] || '-' }}
  220 + </template>
  221 + <template v-if="column.key === 'rule'">
  222 + {{ formatLevelRule(record) }}
  223 + </template>
  224 + <template v-if="column.key === 'action'">
  225 + <a-space>
  226 + <a @click="openLevelEdit(record)">编辑</a>
  227 + <a v-if="record.isDefault !== 1" @click="handleSetDefault(record)">设为默认</a>
  228 + <a-popconfirm title="确认删除该等级?" @confirm="handleDeleteLevel(record)">
  229 + <a style="color:red">删除</a>
  230 + </a-popconfirm>
  231 + </a-space>
  232 + </template>
  233 + </template>
  234 + </a-table>
  235 + </a-modal>
  236 +
  237 + <a-modal v-model:open="levelEditVisible" :title="levelEditingId ? '编辑骑手等级' : '新增骑手等级'"
  238 + @ok="handleSaveLevel" :confirmLoading="levelSaving">
  239 + <a-form :model="levelForm" layout="vertical">
  240 + <a-form-item label="等级编号">
  241 + <a-input-number v-model:value="levelForm.levelId" :min="1" style="width:100%" />
  242 + </a-form-item>
  243 + <a-form-item label="等级名称">
  244 + <a-input v-model:value="levelForm.name" placeholder="如:标准骑手 / 金牌骑手" />
  245 + </a-form-item>
  246 + <a-form-item label="每日转单上限">
  247 + <a-input-number v-model:value="levelForm.transNums" :min="0" style="width:100%" />
  248 + </a-form-item>
  249 + <a-form-item label="收入模式">
  250 + <a-radio-group v-model:value="levelForm.runFeeMode">
  251 + <a-radio :value="1">固定金额</a-radio>
  252 + <a-radio :value="2">按比例</a-radio>
  253 + <a-radio :value="3">按距离</a-radio>
  254 + </a-radio-group>
  255 + </a-form-item>
  256 + <template v-if="levelForm.runFeeMode === 1">
  257 + <a-form-item label="固定收入(元)">
  258 + <a-input-number v-model:value="levelForm.runFixMoney" :min="0" :step="0.1" style="width:100%" />
  259 + </a-form-item>
  260 + </template>
  261 + <template v-else-if="levelForm.runFeeMode === 2">
  262 + <a-form-item label="收入比例(%)">
  263 + <a-input-number v-model:value="levelForm.runRate" :min="0" :max="100" :step="0.1" style="width:100%" />
  264 + </a-form-item>
  265 + </template>
  266 + <template v-else>
  267 + <a-row :gutter="16">
  268 + <a-col :span="12">
  269 + <a-form-item label="起始距离(米)">
  270 + <a-input-number v-model:value="levelForm.distanceBasic" :min="0" style="width:100%" />
  271 + </a-form-item>
  272 + </a-col>
  273 + <a-col :span="12">
  274 + <a-form-item label="基础收入(元)">
  275 + <a-input-number v-model:value="levelForm.distanceBasicMoney" :min="0" :step="0.1" style="width:100%" />
  276 + </a-form-item>
  277 + </a-col>
  278 + </a-row>
  279 + <a-row :gutter="16">
  280 + <a-col :span="12">
  281 + <a-form-item label="超出每公里收入(元)">
  282 + <a-input-number v-model:value="levelForm.distanceMoreMoney" :min="0" :step="0.1" style="width:100%" />
  283 + </a-form-item>
  284 + </a-col>
  285 + <a-col :span="12">
  286 + <a-form-item label="最高收入上限(元)">
  287 + <a-input-number v-model:value="levelForm.distanceMaxMoney" :min="0" :step="0.1" style="width:100%" />
  288 + </a-form-item>
  289 + </a-col>
  290 + </a-row>
  291 + </template>
  292 + </a-form>
  293 + </a-modal>
  294 + </div>
  295 +</template>
  296 +
  297 +<script setup lang="ts">
  298 +import { ref, reactive, onMounted } from 'vue'
  299 +import { message } from 'ant-design-vue'
  300 +import { cityApi, riderLevelApi } from '@/api'
  301 +
  302 +type TimePeriodForm = {
  303 + startText: string
  304 + endText: string
  305 + isOpen: number
  306 + money: number | null
  307 +}
  308 +
  309 +const loading = ref(false)
  310 +const saving = ref(false)
  311 +const list = ref<any[]>([])
  312 +const modalVisible = ref(false)
  313 +const configVisible = ref(false)
  314 +const levelVisible = ref(false)
  315 +const levelEditVisible = ref(false)
  316 +const editingId = ref<number | null>(null)
  317 +const currentCityId = ref<number>(0)
  318 +const levelCityId = ref<number>(0)
  319 +const levelCityName = ref('')
  320 +const form = reactive({ name: '', areaCode: '', rate: 0, listOrder: 0 })
  321 +const config = ref<any>(null)
  322 +const timePeriods = ref<TimePeriodForm[]>([])
  323 +const levelLoading = ref(false)
  324 +const levelSaving = ref(false)
  325 +const levelList = ref<any[]>([])
  326 +const levelEditingId = ref<number | null>(null)
  327 +const levelForm = reactive({
  328 + cityId: 0,
  329 + levelId: 1,
  330 + name: '',
  331 + transNums: 0,
  332 + runFeeMode: 1,
  333 + runFixMoney: 0,
  334 + runRate: 0,
  335 + distanceBasic: 0,
  336 + distanceBasicMoney: 0,
  337 + distanceMoreMoney: 0,
  338 + distanceMaxMoney: 0,
  339 +})
  340 +
  341 +const columns = [
  342 + { title: 'ID', dataIndex: 'id', key: 'id', width: 80 },
  343 + { title: '名称', dataIndex: 'name', key: 'name' },
  344 + { title: '区划码/编号', dataIndex: 'areaCode', key: 'areaCode' },
  345 + { title: '抽成%', dataIndex: 'rate', key: 'rate' },
  346 + { title: '状态', key: 'status' },
  347 + { title: '操作', key: 'action' },
  348 +]
  349 +
  350 +const levelColumns = [
  351 + { title: '等级编号', dataIndex: 'levelId', width: 100 },
  352 + { title: '等级名称', dataIndex: 'name' },
  353 + { title: '默认', key: 'isDefault', width: 90 },
  354 + { title: '转单上限', dataIndex: 'transNums', width: 100 },
  355 + { title: '收入模式', key: 'runFeeMode', width: 110 },
  356 + { title: '规则', key: 'rule' },
  357 + { title: '操作', key: 'action', width: 220 },
  358 +]
  359 +
  360 +const runFeeModeMap: Record<number, string> = {
  361 + 1: '固定金额',
  362 + 2: '按比例',
  363 + 3: '按距离',
  364 +}
  365 +
  366 +async function loadList() {
  367 + loading.value = true
  368 + try {
  369 + const res: any = await cityApi.tree()
  370 + list.value = res.data // 现在直接是一级列表
  371 + } finally {
  372 + loading.value = false
  373 + }
  374 +}
  375 +
  376 +function openAdd() {
  377 + editingId.value = null
  378 + Object.assign(form, { name: '', areaCode: '', rate: 0, listOrder: 0 })
  379 + modalVisible.value = true
  380 +}
  381 +
  382 +function openEdit(record: any) {
  383 + editingId.value = record.id
  384 + Object.assign(form, { name: record.name, areaCode: record.areaCode, rate: record.rate, listOrder: record.listOrder })
  385 + modalVisible.value = true
  386 +}
  387 +
  388 +function openLevelConfig(record: any) {
  389 + levelCityId.value = record.id
  390 + levelCityName.value = record.name
  391 + levelVisible.value = true
  392 + loadLevels()
  393 +}
  394 +
  395 +async function handleSave() {
  396 + saving.value = true
  397 + try {
  398 + if (editingId.value) {
  399 + await cityApi.edit({ ...form, id: editingId.value })
  400 + } else {
  401 + await cityApi.add(form)
  402 + }
  403 + message.success('保存成功')
  404 + modalVisible.value = false
  405 + loadList()
  406 + } finally {
  407 + saving.value = false
  408 + }
  409 +}
  410 +
  411 +async function toggleStatus(record: any) {
  412 + await cityApi.setStatus(record.id, record.status === 1 ? 0 : 1)
  413 + message.success('操作成功')
  414 + loadList()
  415 +}
  416 +
  417 +async function openConfig(record: any) {
  418 + currentCityId.value = record.id
  419 + const res: any = await cityApi.getConfig(record.id)
  420 + config.value = normalizeConfig(res.data)
  421 + timePeriods.value = (config.value.type6.times || []).map((period: any) => ({
  422 + startText: minuteToText(period.start),
  423 + endText: minuteToText(period.end),
  424 + isOpen: period.isOpen === 0 ? 0 : 1,
  425 + money: period.money ?? 0,
  426 + }))
  427 + configVisible.value = true
  428 +}
  429 +
  430 +async function handleConfigSave() {
  431 + try {
  432 + config.value.type = [6]
  433 + config.value.type6.times = buildTimesPayload()
  434 + } catch (err: any) {
  435 + message.error(err.message || '时段配置有误')
  436 + return
  437 + }
  438 +
  439 + saving.value = true
  440 + try {
  441 + await cityApi.updateConfig(currentCityId.value, config.value)
  442 + message.success('配置保存成功')
  443 + configVisible.value = false
  444 + } finally {
  445 + saving.value = false
  446 + }
  447 +}
  448 +
  449 +async function loadLevels() {
  450 + levelLoading.value = true
  451 + try {
  452 + const res: any = await riderLevelApi.list(levelCityId.value)
  453 + levelList.value = Array.isArray(res?.data) ? res.data : []
  454 + } finally {
  455 + levelLoading.value = false
  456 + }
  457 +}
  458 +
  459 +function openLevelAdd() {
  460 + levelEditingId.value = null
  461 + Object.assign(levelForm, {
  462 + cityId: levelCityId.value,
  463 + levelId: (levelList.value[levelList.value.length - 1]?.levelId || 0) + 1,
  464 + name: '',
  465 + transNums: 0,
  466 + runFeeMode: 1,
  467 + runFixMoney: 0,
  468 + runRate: 0,
  469 + distanceBasic: 0,
  470 + distanceBasicMoney: 0,
  471 + distanceMoreMoney: 0,
  472 + distanceMaxMoney: 0,
  473 + })
  474 + levelEditVisible.value = true
  475 +}
  476 +
  477 +function openLevelEdit(record: any) {
  478 + levelEditingId.value = record.id
  479 + Object.assign(levelForm, {
  480 + cityId: levelCityId.value,
  481 + levelId: record.levelId,
  482 + name: record.name,
  483 + transNums: record.transNums,
  484 + runFeeMode: record.runFeeMode,
  485 + runFixMoney: record.runFixMoney ?? 0,
  486 + runRate: record.runRate ?? 0,
  487 + distanceBasic: record.distanceBasic ?? 0,
  488 + distanceBasicMoney: record.distanceBasicMoney ?? 0,
  489 + distanceMoreMoney: record.distanceMoreMoney ?? 0,
  490 + distanceMaxMoney: record.distanceMaxMoney ?? 0,
  491 + })
  492 + levelEditVisible.value = true
  493 +}
  494 +
  495 +async function handleSaveLevel() {
  496 + if (!levelForm.name) {
  497 + message.error('请填写等级名称')
  498 + return
  499 + }
  500 + levelSaving.value = true
  501 + try {
  502 + const payload = { ...levelForm, id: levelEditingId.value || undefined }
  503 + if (levelEditingId.value) {
  504 + await riderLevelApi.edit(payload)
  505 + } else {
  506 + await riderLevelApi.add(payload)
  507 + }
  508 + message.success('保存成功')
  509 + levelEditVisible.value = false
  510 + loadLevels()
  511 + } finally {
  512 + levelSaving.value = false
  513 + }
  514 +}
  515 +
  516 +async function handleSetDefault(record: any) {
  517 + await riderLevelApi.setDefault(record.id, levelCityId.value)
  518 + message.success('设置成功')
  519 + loadLevels()
  520 +}
  521 +
  522 +async function handleDeleteLevel(record: any) {
  523 + await riderLevelApi.del(record.id, levelCityId.value)
  524 + message.success('删除成功')
  525 + loadLevels()
  526 +}
  527 +
  528 +function formatLevelRule(record: any) {
  529 + if (record.runFeeMode === 1) {
  530 + return `固定 ${record.runFixMoney ?? 0} 元`
  531 + }
  532 + if (record.runFeeMode === 2) {
  533 + return `按配送费 ${record.runRate ?? 0}%`
  534 + }
  535 + return `起始${record.distanceBasic ?? 0}米/${record.distanceBasicMoney ?? 0}元,超出每公里${record.distanceMoreMoney ?? 0}元,上限${record.distanceMaxMoney ?? 0}元`
  536 +}
  537 +
  538 +function createDefaultType6() {
  539 + return {
  540 + feeMode: 1,
  541 + fixMoney: 0,
  542 + distanceSwitch: 1,
  543 + distanceBasic: 3,
  544 + distanceBasicMoney: 4,
  545 + distanceMoreMoney: 1.5,
  546 + distanceType: 1,
  547 + weightSwitch: 0,
  548 + weightBasic: 0,
  549 + weightBasicMoney: 0,
  550 + weightMoreMoney: 0,
  551 + weightType: 1,
  552 + times: [],
  553 + }
  554 +}
  555 +
  556 +function normalizeConfig(raw: any) {
  557 + const next = raw || {}
  558 + const type6 = { ...createDefaultType6(), ...(next.type6 || {}) }
  559 + return {
  560 + ...next,
  561 + type: [6],
  562 + type6: {
  563 + ...type6,
  564 + times: Array.isArray(type6.times) ? type6.times : [],
  565 + },
  566 + distanceBasic: next.distanceBasic ?? 3,
  567 + distanceBasicTime: next.distanceBasicTime ?? 30,
  568 + distanceMoreTime: next.distanceMoreTime ?? 10,
  569 + riderDistance: next.riderDistance ?? 3,
  570 + }
  571 +}
  572 +
  573 +function addTimePeriod() {
  574 + timePeriods.value.push({
  575 + startText: '',
  576 + endText: '',
  577 + isOpen: 1,
  578 + money: 0,
  579 + })
  580 +}
  581 +
  582 +function removeTimePeriod(index: number) {
  583 + timePeriods.value.splice(index, 1)
  584 +}
  585 +
  586 +function buildTimesPayload() {
  587 + return timePeriods.value
  588 + .map((period, index) => {
  589 + const hasContent = period.startText || period.endText || period.money
  590 + if (!hasContent) return null
  591 +
  592 + const start = textToMinute(period.startText, `第${index + 1}条时段开始时间格式错误`)
  593 + const end = textToMinute(period.endText, `第${index + 1}条时段结束时间格式错误`)
  594 + if (start >= end) {
  595 + throw new Error(`第${index + 1}条时段结束时间必须晚于开始时间`)
  596 + }
  597 +
  598 + return {
  599 + start,
  600 + end,
  601 + isOpen: period.isOpen === 0 ? 0 : 1,
  602 + money: period.money ?? 0,
  603 + }
  604 + })
  605 + .filter(Boolean)
  606 + .sort((a: any, b: any) => a.start - b.start)
  607 +}
  608 +
  609 +function textToMinute(text: string, errorMessage: string) {
  610 + const match = /^([01]\d|2[0-3]):([0-5]\d)$/.exec((text || '').trim())
  611 + if (!match) {
  612 + throw new Error(errorMessage)
  613 + }
  614 + return Number(match[1]) * 60 + Number(match[2])
  615 +}
  616 +
  617 +function minuteToText(value: number | null | undefined) {
  618 + if (typeof value !== 'number' || Number.isNaN(value)) return ''
  619 + const hour = Math.floor(value / 60)
  620 + const minute = value % 60
  621 + return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`
  622 +}
  623 +
  624 +onMounted(loadList)
  625 +</script>
... ...
src/views/delivery/DeliveryOrderList.vue 0 → 100644
  1 +++ a/src/views/delivery/DeliveryOrderList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="配送订单" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-select v-model:value="filterStatus" placeholder="状态" allowClear style="width:130px" @change="loadList">
  7 + <a-select-option :value="2">待接单</a-select-option>
  8 + <a-select-option :value="3">已接单</a-select-option>
  9 + <a-select-option :value="4">配送中</a-select-option>
  10 + <a-select-option :value="6">已完成</a-select-option>
  11 + <a-select-option :value="10">已取消</a-select-option>
  12 + </a-select>
  13 + <a-input-search v-model:value="keyword" placeholder="外部订单号" @search="loadList" style="width:220px" />
  14 + <a-button type="primary" @click="queryByNo" :loading="querying">查询</a-button>
  15 + </a-space>
  16 + </template>
  17 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  18 + <template #bodyCell="{ column, record }">
  19 + <template v-if="column.key === 'status'">
  20 + <a-tag :color="statusColor[record.status]">{{ statusMap[record.status] }}</a-tag>
  21 + </template>
  22 + <template v-if="column.key === 'action'">
  23 + <a-popconfirm v-if="record.status === 2" title="确认取消该配送订单?" @confirm="cancelOrder(record)">
  24 + <a style="color:red">取消</a>
  25 + </a-popconfirm>
  26 + <span v-else>-</span>
  27 + </template>
  28 + </template>
  29 + </a-table>
  30 + </a-card>
  31 +
  32 + <!-- 查询结果弹窗 -->
  33 + <a-modal v-model:open="queryVisible" title="配送订单详情" :footer="null">
  34 + <a-descriptions :column="1" bordered size="small" v-if="queryResult">
  35 + <a-descriptions-item label="配送订单ID">{{ queryResult.deliveryOrderId }}</a-descriptions-item>
  36 + <a-descriptions-item label="外部订单号">{{ queryResult.outOrderNo }}</a-descriptions-item>
  37 + <a-descriptions-item label="状态">{{ statusMap[queryResult.status] }}</a-descriptions-item>
  38 + <a-descriptions-item label="配送费">¥{{ queryResult.totalFee }}</a-descriptions-item>
  39 + <a-descriptions-item label="距离">{{ queryResult.distance }} km</a-descriptions-item>
  40 + <a-descriptions-item label="预计时间">{{ queryResult.estimatedMinutes }} 分钟</a-descriptions-item>
  41 + </a-descriptions>
  42 + </a-modal>
  43 + </div>
  44 +</template>
  45 +
  46 +<script setup lang="ts">
  47 +import { ref, onMounted } from 'vue'
  48 +import { message } from 'ant-design-vue'
  49 +import { deliveryOrderApi, riderApi } from '@/api'
  50 +
  51 +const loading = ref(false)
  52 +const querying = ref(false)
  53 +const list = ref<any[]>([])
  54 +const filterStatus = ref<number | undefined>()
  55 +const keyword = ref('')
  56 +const queryVisible = ref(false)
  57 +const queryResult = ref<any>(null)
  58 +
  59 +const statusMap: Record<number, string> = {
  60 + 2: '待接单', 3: '已接单', 4: '配送中', 6: '已完成', 7: '退款申请',
  61 + 8: '退款成功', 9: '退款拒绝', 10: '已取消'
  62 +}
  63 +const statusColor: Record<number, string> = {
  64 + 2: 'blue', 3: 'cyan', 4: 'processing', 6: 'green',
  65 + 7: 'orange', 8: 'green', 9: 'red', 10: 'red'
  66 +}
  67 +
  68 +const columns = [
  69 + { title: 'ID', dataIndex: 'id', width: 80 },
  70 + { title: '外部订单号', dataIndex: 'outOrderNo' },
  71 + { title: '接入方', dataIndex: 'appKey', ellipsis: true },
  72 + { title: '收件人', dataIndex: 'recipName' },
  73 + { title: '配送费', dataIndex: 'moneyDelivery' },
  74 + { title: '状态', key: 'status' },
  75 + { title: '操作', key: 'action' },
  76 +]
  77 +
  78 +async function loadList() {
  79 + loading.value = true
  80 + try {
  81 + const res: any = await riderApi.deliveryOrderList({
  82 + status: filterStatus.value,
  83 + outOrderNo: keyword.value || undefined,
  84 + page: 1
  85 + })
  86 + list.value = Array.isArray(res?.data) ? res.data : []
  87 + } finally { loading.value = false }
  88 +}
  89 +
  90 +async function queryByNo() {
  91 + if (!keyword.value) { message.warning('请输入外部订单号'); return }
  92 + querying.value = true
  93 + try {
  94 + const res: any = await deliveryOrderApi.query(keyword.value)
  95 + queryResult.value = res.data
  96 + queryVisible.value = true
  97 + } finally { querying.value = false }
  98 +}
  99 +
  100 +async function cancelOrder(record: any) {
  101 + await deliveryOrderApi.cancel(record.outOrderNo)
  102 + message.success('取消成功')
  103 + loadList()
  104 +}
  105 +
  106 +onMounted(loadList)
  107 +</script>
... ...
src/views/merchant/EnterList.vue 0 → 100644
  1 +++ a/src/views/merchant/EnterList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="入驻申请" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-select v-model:value="filterStatus" placeholder="状态" allowClear style="width:120px" @change="loadList">
  7 + <a-select-option :value="0">未处理</a-select-option>
  8 + <a-select-option :value="1">已通过</a-select-option>
  9 + <a-select-option :value="-1">已拒绝</a-select-option>
  10 + </a-select>
  11 + <a-select v-model:value="filterType" placeholder="类型" allowClear style="width:120px" @change="loadList">
  12 + <a-select-option :value="1">商家入驻</a-select-option>
  13 + <a-select-option :value="2">骑手入驻</a-select-option>
  14 + <a-select-option :value="3">商务合作</a-select-option>
  15 + </a-select>
  16 + </a-space>
  17 + </template>
  18 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  19 + <template #bodyCell="{ column, record }">
  20 + <template v-if="column.key === 'type'">
  21 + <a-tag>{{ typeMap[record.type] }}</a-tag>
  22 + </template>
  23 + <template v-if="column.key === 'status'">
  24 + <a-tag :color="record.status === 1 ? 'green' : record.status === -1 ? 'red' : 'orange'">
  25 + {{ statusMap[record.status] }}
  26 + </a-tag>
  27 + </template>
  28 + <template v-if="column.key === 'action'">
  29 + <a-space v-if="record.status === 0">
  30 + <a-popconfirm title="确认通过?" @confirm="handle(record.id, 1)">
  31 + <a style="color:green">通过</a>
  32 + </a-popconfirm>
  33 + <a-popconfirm title="确认拒绝?" @confirm="handle(record.id, -1)">
  34 + <a style="color:red">拒绝</a>
  35 + </a-popconfirm>
  36 + </a-space>
  37 + <span v-else>-</span>
  38 + </template>
  39 + </template>
  40 + </a-table>
  41 + </a-card>
  42 + </div>
  43 +</template>
  44 +
  45 +<script setup lang="ts">
  46 +import { ref, onMounted } from 'vue'
  47 +import { message } from 'ant-design-vue'
  48 +import { merchantApi } from '@/api'
  49 +
  50 +const loading = ref(false)
  51 +const list = ref<any[]>([])
  52 +const filterStatus = ref<number | undefined>()
  53 +const filterType = ref<number | undefined>()
  54 +
  55 +const typeMap: Record<number, string> = { 1: '商家入驻', 2: '骑手入驻', 3: '商务合作' }
  56 +const statusMap: Record<number, string> = { 0: '未处理', 1: '已通过', [-1]: '已拒绝' }
  57 +
  58 +const columns = [
  59 + { title: 'ID', dataIndex: 'id', width: 80 },
  60 + { title: '联系人', dataIndex: 'name' },
  61 + { title: '手机', dataIndex: 'mobile' },
  62 + { title: '店铺名', dataIndex: 'storeName' },
  63 + { title: '类型', key: 'type' },
  64 + { title: '状态', key: 'status' },
  65 + { title: '操作', key: 'action' },
  66 +]
  67 +
  68 +async function loadList() {
  69 + loading.value = true
  70 + try {
  71 + const res: any = await merchantApi.enterList({ status: filterStatus.value, type: filterType.value, page: 1 })
  72 + list.value = res.data
  73 + } finally { loading.value = false }
  74 +}
  75 +
  76 +async function handle(id: number, status: number) {
  77 + await merchantApi.handleEnter(id, status)
  78 + message.success('操作成功')
  79 + loadList()
  80 +}
  81 +
  82 +onMounted(loadList)
  83 +</script>
... ...
src/views/merchant/StoreList.vue 0 → 100644
  1 +++ a/src/views/merchant/StoreList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="店铺管理" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-select v-model:value="filterCityId" placeholder="选择城市" allowClear style="width:150px" @change="loadList">
  7 + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
  8 + </a-select>
  9 + <a-input-search v-model:value="keyword" placeholder="搜索店铺名" @search="loadList" style="width:200px" />
  10 + <a-button type="primary" @click="openAdd">新增店铺</a-button>
  11 + </a-space>
  12 + </template>
  13 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  14 + <template #bodyCell="{ column, record }">
  15 + <template v-if="column.key === 'operatingState'">
  16 + <a-tag :color="record.operatingState === 1 ? 'green' : 'default'">
  17 + {{ record.operatingState === 1 ? '营业中' : '打烊' }}
  18 + </a-tag>
  19 + </template>
  20 + <template v-if="column.key === 'shippingType'">
  21 + {{ record.shippingType === 1 ? '外卖配送' : '到店自提' }}
  22 + </template>
  23 + <template v-if="column.key === 'action'">
  24 + <a-space>
  25 + <a @click="openEdit(record)">编辑</a>
  26 + <a @click="openFeeConfig(record)">费用配置</a>
  27 + <a-popconfirm
  28 + :title="record.operatingState === 1 ? '确认打烊?' : '确认开始营业?'"
  29 + @confirm="toggleState(record)">
  30 + <a>{{ record.operatingState === 1 ? '打烊' : '营业' }}</a>
  31 + </a-popconfirm>
  32 + <a-popconfirm title="确认删除?" @confirm="handleDel(record.id)">
  33 + <a style="color:red">删除</a>
  34 + </a-popconfirm>
  35 + </a-space>
  36 + </template>
  37 + </template>
  38 + </a-table>
  39 + </a-card>
  40 +
  41 + <!-- 新增/编辑弹窗 -->
  42 + <a-modal v-model:open="modalVisible" :title="editingId ? '编辑店铺' : '新增店铺'"
  43 + @ok="handleSave" :confirmLoading="saving" width="600px">
  44 + <a-form :model="form" layout="vertical">
  45 + <a-form-item label="店铺名称"><a-input v-model:value="form.name" /></a-form-item>
  46 + <a-form-item label="所属城市">
  47 + <a-select v-model:value="form.cityId" placeholder="选择城市">
  48 + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
  49 + </a-select>
  50 + </a-form-item>
  51 + <a-form-item label="外部门店编号(选填,接入方对账用)">
  52 + <a-input v-model:value="form.outStoreId" placeholder="接入方自己系统的门店ID" />
  53 + </a-form-item>
  54 + <a-form-item label="店铺地址"><a-input v-model:value="form.address" /></a-form-item>
  55 + <a-row :gutter="16">
  56 + <a-col :span="12">
  57 + <a-form-item label="经度"><a-input v-model:value="form.lng" /></a-form-item>
  58 + </a-col>
  59 + <a-col :span="12">
  60 + <a-form-item label="纬度"><a-input v-model:value="form.lat" /></a-form-item>
  61 + </a-col>
  62 + </a-row>
  63 + <a-form-item label="配送类型">
  64 + <a-radio-group v-model:value="form.shippingType">
  65 + <a-radio :value="1">外卖配送</a-radio>
  66 + <a-radio :value="2">到店自提</a-radio>
  67 + </a-radio-group>
  68 + </a-form-item>
  69 + <a-form-item label="自动接单">
  70 + <a-switch v-model:checked="form.automaticOrder" :checked-value="1" :un-checked-value="0" />
  71 + </a-form-item>
  72 + <a-form-item label="商家账号手机号(新增时创建登录账号)" v-if="!editingId">
  73 + <a-input v-model:value="form.accountMobile" placeholder="选填" />
  74 + </a-form-item>
  75 + <a-form-item label="店铺简介"><a-textarea v-model:value="form.about" :rows="3" /></a-form-item>
  76 + </a-form>
  77 + </a-modal>
  78 +
  79 + <!-- 费用配置弹窗 -->
  80 + <a-modal v-model:open="feeVisible" title="费用配置" @ok="handleFeeSave" :confirmLoading="saving">
  81 + <a-form layout="vertical">
  82 + <a-form-item label="免运费门槛(元,0=不免运费)">
  83 + <a-input-number v-model:value="feeForm.freeShipping" :min="0" style="width:100%" />
  84 + </a-form-item>
  85 + <a-form-item label="起送金额(元,0=不限)">
  86 + <a-input-number v-model:value="feeForm.upToSend" :min="0" style="width:100%" />
  87 + </a-form-item>
  88 + </a-form>
  89 + </a-modal>
  90 + </div>
  91 +</template>
  92 +
  93 +<script setup lang="ts">
  94 +import { ref, reactive, onMounted } from 'vue'
  95 +import { message } from 'ant-design-vue'
  96 +import { merchantApi, cityApi } from '@/api'
  97 +
  98 +const loading = ref(false)
  99 +const saving = ref(false)
  100 +const list = ref<any[]>([])
  101 +const cityList = ref<any[]>([])
  102 +const keyword = ref('')
  103 +const filterCityId = ref<number | undefined>()
  104 +const modalVisible = ref(false)
  105 +const feeVisible = ref(false)
  106 +const editingId = ref<number | null>(null)
  107 +const currentStoreId = ref(0)
  108 +const form = reactive<any>({ name: '', cityId: undefined, address: '', lng: '', lat: '', shippingType: 1, automaticOrder: 0, accountMobile: '', about: '', outStoreId: '' })
  109 +const feeForm = reactive({ freeShipping: 0, upToSend: 0 })
  110 +
  111 +const columns = [
  112 + { title: 'ID', dataIndex: 'id', width: 80 },
  113 + { title: '店铺名', dataIndex: 'name' },
  114 + { title: '城市', dataIndex: 'cityId' },
  115 + { title: '外部编号', dataIndex: 'outStoreId', ellipsis: true },
  116 + { title: '接入方', dataIndex: 'appKey', ellipsis: true },
  117 + { title: '地址', dataIndex: 'address', ellipsis: true },
  118 + { title: '配送', key: 'shippingType' },
  119 + { title: '状态', key: 'operatingState' },
  120 + { title: '操作', key: 'action' },
  121 +]
  122 +
  123 +async function loadList() {
  124 + loading.value = true
  125 + try {
  126 + const res: any = await merchantApi.storeList({ cityId: filterCityId.value, keyword: keyword.value, page: 1 })
  127 + list.value = res.data
  128 + } finally { loading.value = false }
  129 +}
  130 +
  131 +async function loadCities() {
  132 + const res: any = await cityApi.openList()
  133 + cityList.value = res.data
  134 +}
  135 +
  136 +function openAdd() {
  137 + editingId.value = null
  138 + Object.assign(form, { name: '', cityId: undefined, address: '', lng: '', lat: '', shippingType: 1, automaticOrder: 0, accountMobile: '', about: '', outStoreId: '' })
  139 + modalVisible.value = true
  140 +}
  141 +
  142 +function openEdit(record: any) {
  143 + editingId.value = record.id
  144 + Object.assign(form, record)
  145 + modalVisible.value = true
  146 +}
  147 +
  148 +async function handleSave() {
  149 + saving.value = true
  150 + try {
  151 + if (editingId.value) {
  152 + await merchantApi.editStore({ ...form, id: editingId.value })
  153 + } else {
  154 + await merchantApi.addStore(form)
  155 + }
  156 + message.success('保存成功')
  157 + modalVisible.value = false
  158 + loadList()
  159 + } finally { saving.value = false }
  160 +}
  161 +
  162 +async function toggleState(record: any) {
  163 + await merchantApi.setOperatingState(record.id, record.operatingState === 1 ? 0 : 1)
  164 + message.success('操作成功')
  165 + loadList()
  166 +}
  167 +
  168 +async function handleDel(id: number) {
  169 + await merchantApi.delStore(id)
  170 + message.success('删除成功')
  171 + loadList()
  172 +}
  173 +
  174 +function openFeeConfig(record: any) {
  175 + currentStoreId.value = record.id
  176 + feeForm.freeShipping = record.freeShipping || 0
  177 + feeForm.upToSend = record.upToSend || 0
  178 + feeVisible.value = true
  179 +}
  180 +
  181 +async function handleFeeSave() {
  182 + saving.value = true
  183 + try {
  184 + await merchantApi.updateFeeConfig(currentStoreId.value, feeForm.freeShipping, feeForm.upToSend)
  185 + message.success('保存成功')
  186 + feeVisible.value = false
  187 + loadList()
  188 + } finally { saving.value = false }
  189 +}
  190 +
  191 +onMounted(() => { loadList(); loadCities() })
  192 +</script>
... ...
src/views/open/OpenAppList.vue 0 → 100644
  1 +++ a/src/views/open/OpenAppList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="开放平台应用管理" :bordered="false">
  4 + <template #extra>
  5 + <a-button type="primary" @click="openAdd">创建应用</a-button>
  6 + </template>
  7 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  8 + <template #bodyCell="{ column, record }">
  9 + <template v-if="column.key === 'status'">
  10 + <a-tag :color="record.status === 1 ? 'green' : 'red'">
  11 + {{ record.status === 1 ? '正常' : '禁用' }}
  12 + </a-tag>
  13 + </template>
  14 + <template v-if="column.key === 'appKey'">
  15 + <a-typography-text copyable>{{ record.appKey }}</a-typography-text>
  16 + </template>
  17 + <template v-if="column.key === 'action'">
  18 + <a-space>
  19 + <a @click="handleResetSecret(record)">重置密钥</a>
  20 + <a @click="openWebhook(record)">Webhook</a>
  21 + <a @click="openLogs(record)">推送日志</a>
  22 + <a-popconfirm
  23 + :title="record.status === 1 ? '确认禁用?' : '确认启用?'"
  24 + @confirm="toggleStatus(record)">
  25 + <a :style="record.status === 1 ? 'color:red' : ''">
  26 + {{ record.status === 1 ? '禁用' : '启用' }}
  27 + </a>
  28 + </a-popconfirm>
  29 + </a-space>
  30 + </template>
  31 + </template>
  32 + </a-table>
  33 + </a-card>
  34 +
  35 + <!-- 创建应用 -->
  36 + <a-modal v-model:open="addVisible" title="创建应用" @ok="handleCreate" :confirmLoading="saving">
  37 + <a-form :model="addForm" layout="vertical">
  38 + <a-form-item label="应用名称"><a-input v-model:value="addForm.appName" /></a-form-item>
  39 + <a-form-item label="关联城市/租户(必填)">
  40 + <a-select v-model:value="addForm.cityId" placeholder="选择城市" style="width:100%">
  41 + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
  42 + </a-select>
  43 + </a-form-item>
  44 + <a-form-item label="备注"><a-input v-model:value="addForm.remark" /></a-form-item>
  45 + </a-form>
  46 + </a-modal>
  47 +
  48 + <!-- Webhook配置 -->
  49 + <a-modal v-model:open="webhookVisible" title="Webhook配置" @ok="handleWebhookSave" :confirmLoading="saving">
  50 + <a-form layout="vertical">
  51 + <a-form-item label="回调地址">
  52 + <a-input v-model:value="webhookForm.webhookUrl" placeholder="https://your-server.com/webhook" />
  53 + </a-form-item>
  54 + <a-form-item label="订阅事件(JSON数组)">
  55 + <a-textarea v-model:value="webhookForm.webhookEvents"
  56 + placeholder='["order.paid","order.completed","order.cancelled"]' :rows="4" />
  57 + </a-form-item>
  58 + <a-alert message="支持事件:order.paid / order.accepted / order.completed / order.cancelled / order.refund" type="info" show-icon />
  59 + </a-form>
  60 + </a-modal>
  61 +
  62 + <!-- 推送日志 -->
  63 + <a-modal v-model:open="logsVisible" title="Webhook推送日志" :footer="null" width="800px">
  64 + <a-table :dataSource="logs" :columns="logColumns" rowKey="id" size="small" :pagination="false">
  65 + <template #bodyCell="{ column, record }">
  66 + <template v-if="column.key === 'status'">
  67 + <a-tag :color="record.status === 1 ? 'green' : 'red'">
  68 + {{ record.status === 1 ? '成功' : '失败' }}
  69 + </a-tag>
  70 + </template>
  71 + <template v-if="column.key === 'action'">
  72 + <a v-if="record.status === 0" @click="retryLog(record.id)">重试</a>
  73 + </template>
  74 + </template>
  75 + </a-table>
  76 + </a-modal>
  77 + </div>
  78 +</template>
  79 +
  80 +<script setup lang="ts">
  81 +import { ref, reactive, onMounted } from 'vue'
  82 +import { message, Modal } from 'ant-design-vue'
  83 +import { openApi, cityApi } from '@/api'
  84 +
  85 +const loading = ref(false)
  86 +const saving = ref(false)
  87 +const list = ref<any[]>([])
  88 +const logs = ref<any[]>([])
  89 +const cityList = ref<any[]>([])
  90 +const addVisible = ref(false)
  91 +const webhookVisible = ref(false)
  92 +const logsVisible = ref(false)
  93 +const currentAppId = ref(0)
  94 +const addForm = reactive({ appName: '', cityId: undefined as number | undefined, remark: '' })
  95 +const webhookForm = reactive({ webhookUrl: '', webhookEvents: '' })
  96 +
  97 +const columns = [
  98 + { title: 'ID', dataIndex: 'id', width: 80 },
  99 + { title: '应用名称', dataIndex: 'appName' },
  100 + { title: 'AppKey', key: 'appKey' },
  101 + { title: '城市/租户', dataIndex: 'cityId' },
  102 + { title: '状态', key: 'status' },
  103 + { title: '回调地址', dataIndex: 'webhookUrl', ellipsis: true },
  104 + { title: '操作', key: 'action' },
  105 +]
  106 +
  107 +const logColumns = [
  108 + { title: 'ID', dataIndex: 'id', width: 80 },
  109 + { title: '事件', dataIndex: 'event' },
  110 + { title: '业务ID', dataIndex: 'bizId' },
  111 + { title: '响应码', dataIndex: 'responseCode' },
  112 + { title: '状态', key: 'status' },
  113 + { title: '重试', dataIndex: 'retryCount' },
  114 + { title: '操作', key: 'action' },
  115 +]
  116 +
  117 +async function loadList() {
  118 + loading.value = true
  119 + try {
  120 + const res: any = await openApi.list()
  121 + list.value = res.data
  122 + } finally { loading.value = false }
  123 +}
  124 +
  125 +async function loadCities() {
  126 + const res: any = await cityApi.openList()
  127 + cityList.value = res.data
  128 +}
  129 +
  130 +function openAdd() {
  131 + Object.assign(addForm, { appName: '', cityId: undefined, remark: '' })
  132 + addVisible.value = true
  133 +}
  134 +
  135 +async function handleCreate() {
  136 + if (!addForm.cityId) { message.error('请选择城市/租户'); return }
  137 + saving.value = true
  138 + try {
  139 + await openApi.create(addForm)
  140 + message.success('创建成功')
  141 + addVisible.value = false
  142 + loadList()
  143 + } finally { saving.value = false }
  144 +}
  145 +
  146 +async function handleResetSecret(record: any) {
  147 + Modal.confirm({
  148 + title: '确认重置 AppSecret?',
  149 + content: '重置后旧密钥立即失效,请及时更新接入方配置',
  150 + onOk: async () => {
  151 + const res: any = await openApi.resetSecret(record.id)
  152 + Modal.info({ title: '新 AppSecret', content: res.data })
  153 + }
  154 + })
  155 +}
  156 +
  157 +async function toggleStatus(record: any) {
  158 + await openApi.setStatus(record.id, record.status === 1 ? 0 : 1)
  159 + message.success('操作成功')
  160 + loadList()
  161 +}
  162 +
  163 +function openWebhook(record: any) {
  164 + currentAppId.value = record.id
  165 + webhookForm.webhookUrl = record.webhookUrl || ''
  166 + webhookForm.webhookEvents = record.webhookEvents || ''
  167 + webhookVisible.value = true
  168 +}
  169 +
  170 +async function handleWebhookSave() {
  171 + saving.value = true
  172 + try {
  173 + await openApi.updateWebhook(currentAppId.value, webhookForm.webhookUrl, webhookForm.webhookEvents)
  174 + message.success('保存成功')
  175 + webhookVisible.value = false
  176 + loadList()
  177 + } finally { saving.value = false }
  178 +}
  179 +
  180 +async function openLogs(record: any) {
  181 + currentAppId.value = record.id
  182 + const res: any = await openApi.webhookLogs(record.id)
  183 + logs.value = res.data
  184 + logsVisible.value = true
  185 +}
  186 +
  187 +async function retryLog(logId: number) {
  188 + await openApi.retryWebhook(logId)
  189 + message.success('重试已触发')
  190 + const res: any = await openApi.webhookLogs(currentAppId.value)
  191 + logs.value = res.data
  192 +}
  193 +
  194 +onMounted(() => { loadList(); loadCities() })
  195 +</script>
... ...
src/views/order/OrderList.vue 0 → 100644
  1 +++ a/src/views/order/OrderList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="订单管理" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-select v-model:value="filterStatus" placeholder="订单状态" allowClear style="width:130px" @change="loadList">
  7 + <a-select-option :value="2">已支付</a-select-option>
  8 + <a-select-option :value="3">已接单</a-select-option>
  9 + <a-select-option :value="4">服务中</a-select-option>
  10 + <a-select-option :value="6">已完成</a-select-option>
  11 + <a-select-option :value="7">退款申请</a-select-option>
  12 + <a-select-option :value="10">已取消</a-select-option>
  13 + </a-select>
  14 + <a-select v-model:value="filterTrans" placeholder="转单状态" allowClear style="width:130px" @change="loadList">
  15 + <a-select-option :value="2">转单申请中</a-select-option>
  16 + <a-select-option :value="1">已转单</a-select-option>
  17 + <a-select-option :value="3">转单拒绝</a-select-option>
  18 + </a-select>
  19 + <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" style="width:200px" />
  20 + </a-space>
  21 + </template>
  22 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  23 + <template #bodyCell="{ column, record }">
  24 + <template v-if="column.key === 'status'">
  25 + <a-tag :color="statusColor[record.status]">{{ statusMap[record.status] }}</a-tag>
  26 + </template>
  27 + <template v-if="column.key === 'isTrans'">
  28 + <a-tag v-if="record.isTrans === 2" color="orange">申请中</a-tag>
  29 + <a-tag v-else-if="record.isTrans === 1" color="blue">已转单</a-tag>
  30 + <a-tag v-else-if="record.isTrans === 3" color="red">已拒绝</a-tag>
  31 + <span v-else>-</span>
  32 + </template>
  33 + <template v-if="column.key === 'action'">
  34 + <a-space>
  35 + <a @click="openDesignate(record)" v-if="record.status === 2">指派骑手</a>
  36 + <template v-if="record.isTrans === 2 && record.status === 4">
  37 + <a-popconfirm title="通过转单申请?" @confirm="handleTrans(record.id, 1)">
  38 + <a style="color:green">通过转单</a>
  39 + </a-popconfirm>
  40 + <a-popconfirm title="拒绝转单申请?" @confirm="handleTrans(record.id, 3)">
  41 + <a style="color:red">拒绝转单</a>
  42 + </a-popconfirm>
  43 + </template>
  44 + <a v-if="record.status === 7" @click="openRefund(record)">查看退款</a>
  45 + </a-space>
  46 + </template>
  47 + </template>
  48 + </a-table>
  49 + </a-card>
  50 +
  51 + <!-- 指派骑手弹窗 -->
  52 + <a-modal v-model:open="designateVisible" title="指派骑手" @ok="handleDesignate" :confirmLoading="saving">
  53 + <a-form layout="vertical">
  54 + <a-form-item label="骑手ID">
  55 + <a-input-number v-model:value="designateRiderId" style="width:100%" placeholder="输入骑手ID" />
  56 + </a-form-item>
  57 + </a-form>
  58 + </a-modal>
  59 +
  60 + <!-- 退款记录弹窗 -->
  61 + <a-modal v-model:open="refundVisible" title="退款记录" :footer="null">
  62 + <a-descriptions :column="1" bordered size="small" v-if="refundRecord">
  63 + <a-descriptions-item label="订单号">{{ refundRecord.orderNo }}</a-descriptions-item>
  64 + <a-descriptions-item label="申请角色">{{ refundRecord.role === 1 ? '用户' : '骑手' }}</a-descriptions-item>
  65 + <a-descriptions-item label="退款原因">{{ refundRecord.reason }}</a-descriptions-item>
  66 + <a-descriptions-item label="退款金额">¥{{ refundRecord.money }}</a-descriptions-item>
  67 + <a-descriptions-item label="状态">
  68 + <a-tag :color="refundRecord.status === 1 ? 'green' : refundRecord.status === 2 ? 'red' : 'orange'">
  69 + {{ ({'0': '待处理', '1': '已通过', '2': '已拒绝'} as Record<string, string>)[String(refundRecord.status)] }}
  70 + </a-tag>
  71 + </a-descriptions-item>
  72 + <a-descriptions-item label="处理备注">{{ refundRecord.remark || '-' }}</a-descriptions-item>
  73 + </a-descriptions>
  74 + <a-space style="margin-top:16px" v-if="refundRecord && refundRecord.status === 0">
  75 + <a-popconfirm title="确认通过退款?" @confirm="handleRefund(1)">
  76 + <a-button type="primary">通过退款</a-button>
  77 + </a-popconfirm>
  78 + <a-button danger @click="openReject">拒绝退款</a-button>
  79 + </a-space>
  80 + </a-modal>
  81 +
  82 + <!-- 拒绝退款弹窗 -->
  83 + <a-modal v-model:open="rejectVisible" title="拒绝退款" @ok="handleRefund(2)" :confirmLoading="saving">
  84 + <a-textarea v-model:value="rejectRemark" :rows="3" placeholder="填写拒绝原因" />
  85 + </a-modal>
  86 + </div>
  87 +</template>
  88 +
  89 +<script setup lang="ts">
  90 +import { ref, onMounted } from 'vue'
  91 +import { message } from 'ant-design-vue'
  92 +import { riderApi, refundApi } from '@/api'
  93 +
  94 +const loading = ref(false)
  95 +const saving = ref(false)
  96 +const list = ref<any[]>([])
  97 +const filterStatus = ref<number | undefined>()
  98 +const filterTrans = ref<number | undefined>()
  99 +const keyword = ref('')
  100 +const designateVisible = ref(false)
  101 +const refundVisible = ref(false)
  102 +const rejectVisible = ref(false)
  103 +const rejectRemark = ref('')
  104 +const designateOrderId = ref(0)
  105 +const designateRiderId = ref<number | undefined>()
  106 +const refundRecord = ref<any>(null)
  107 +const currentRefundRecordId = ref(0)
  108 +
  109 +const statusMap: Record<number, string> = {
  110 + 1: '待支付', 2: '已支付', 3: '已接单', 4: '服务中',
  111 + 6: '已完成', 7: '退款申请', 8: '退款成功', 9: '退款拒绝', 10: '已取消'
  112 +}
  113 +const statusColor: Record<number, string> = {
  114 + 1: 'default', 2: 'blue', 3: 'cyan', 4: 'processing',
  115 + 6: 'green', 7: 'orange', 8: 'green', 9: 'red', 10: 'red'
  116 +}
  117 +
  118 +const columns = [
  119 + { title: 'ID', dataIndex: 'id', width: 80 },
  120 + { title: '订单号', dataIndex: 'orderNo', ellipsis: true },
  121 + { title: '状态', key: 'status' },
  122 + { title: '骑手ID', dataIndex: 'riderId' },
  123 + { title: '转单', key: 'isTrans' },
  124 + { title: '配送费', dataIndex: 'moneyDelivery' },
  125 + { title: '操作', key: 'action' },
  126 +]
  127 +
  128 +async function loadList() {
  129 + loading.value = true
  130 + try {
  131 + const res: any = await riderApi.orderList({
  132 + status: filterStatus.value,
  133 + isTrans: filterTrans.value,
  134 + keyword: keyword.value,
  135 + page: 1
  136 + })
  137 + list.value = Array.isArray(res?.data) ? res.data : []
  138 + } finally { loading.value = false }
  139 +}
  140 +
  141 +function openDesignate(record: any) {
  142 + designateOrderId.value = record.id
  143 + designateRiderId.value = undefined
  144 + designateVisible.value = true
  145 +}
  146 +
  147 +async function handleDesignate() {
  148 + if (!designateRiderId.value) { message.error('请输入骑手ID'); return }
  149 + saving.value = true
  150 + try {
  151 + await riderApi.designate(designateOrderId.value, designateRiderId.value)
  152 + message.success('指派成功')
  153 + designateVisible.value = false
  154 + loadList()
  155 + } finally { saving.value = false }
  156 +}
  157 +
  158 +async function handleTrans(orderId: number, trans: number) {
  159 + await riderApi.setTrans(orderId, trans)
  160 + message.success('操作成功')
  161 + loadList()
  162 +}
  163 +
  164 +async function openRefund(record: any) {
  165 + const res: any = await refundApi.record(record.id)
  166 + refundRecord.value = res.data
  167 + if (refundRecord.value) currentRefundRecordId.value = refundRecord.value.id
  168 + refundVisible.value = true
  169 +}
  170 +
  171 +function openReject() {
  172 + rejectRemark.value = ''
  173 + rejectVisible.value = true
  174 +}
  175 +
  176 +async function handleRefund(status: number) {
  177 + saving.value = true
  178 + try {
  179 + await refundApi.handle(currentRefundRecordId.value, status, rejectRemark.value)
  180 + message.success('操作成功')
  181 + refundVisible.value = false
  182 + rejectVisible.value = false
  183 + loadList()
  184 + } finally { saving.value = false }
  185 +}
  186 +
  187 +onMounted(loadList)
  188 +</script>
... ...
src/views/order/RefundList.vue 0 → 100644
  1 +++ a/src/views/order/RefundList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="退款管理" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-select v-model:value="filterStatus" placeholder="状态" allowClear style="width:120px" @change="loadList">
  7 + <a-select-option :value="0">待处理</a-select-option>
  8 + <a-select-option :value="1">已通过</a-select-option>
  9 + <a-select-option :value="2">已拒绝</a-select-option>
  10 + </a-select>
  11 + <a-input-search v-model:value="keyword" placeholder="订单号" @search="loadList" style="width:200px" />
  12 + </a-space>
  13 + </template>
  14 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  15 + <template #bodyCell="{ column, record }">
  16 + <template v-if="column.key === 'role'">
  17 + {{ record.role === 1 ? '用户' : '骑手' }}
  18 + </template>
  19 + <template v-if="column.key === 'status'">
  20 + <a-tag :color="record.status === 1 ? 'green' : record.status === 2 ? 'red' : 'orange'">
  21 + {{ statusMap[record.status] }}
  22 + </a-tag>
  23 + </template>
  24 + <template v-if="column.key === 'action'">
  25 + <a-space v-if="record.status === 0">
  26 + <a-popconfirm title="确认通过退款?" @confirm="handle(record.id, 1)">
  27 + <a style="color:green">通过</a>
  28 + </a-popconfirm>
  29 + <a @click="openReject(record)">拒绝</a>
  30 + </a-space>
  31 + <span v-else>{{ record.remark || '-' }}</span>
  32 + </template>
  33 + </template>
  34 + </a-table>
  35 + </a-card>
  36 +
  37 + <!-- 拒绝弹窗 -->
  38 + <a-modal v-model:open="rejectVisible" title="拒绝退款" @ok="handleReject" :confirmLoading="saving">
  39 + <a-form layout="vertical">
  40 + <a-form-item label="拒绝原因">
  41 + <a-textarea v-model:value="rejectRemark" :rows="3" placeholder="请填写拒绝原因" />
  42 + </a-form-item>
  43 + </a-form>
  44 + </a-modal>
  45 + </div>
  46 +</template>
  47 +
  48 +<script setup lang="ts">
  49 +import { ref, onMounted } from 'vue'
  50 +import { message } from 'ant-design-vue'
  51 +import { refundApi } from '@/api'
  52 +import request from '@/utils/request'
  53 +
  54 +const loading = ref(false)
  55 +const saving = ref(false)
  56 +const list = ref<any[]>([])
  57 +const filterStatus = ref<number | undefined>()
  58 +const keyword = ref('')
  59 +const rejectVisible = ref(false)
  60 +const rejectRemark = ref('')
  61 +const currentRecordId = ref(0)
  62 +
  63 +const statusMap: Record<number, string> = { 0: '待处理', 1: '已通过', 2: '已拒绝' }
  64 +
  65 +const columns = [
  66 + { title: 'ID', dataIndex: 'id', width: 80 },
  67 + { title: '订单号', dataIndex: 'orderNo', ellipsis: true },
  68 + { title: '申请角色', key: 'role' },
  69 + { title: '原因', dataIndex: 'reason', ellipsis: true },
  70 + { title: '退款金额', dataIndex: 'money' },
  71 + { title: '状态', key: 'status' },
  72 + { title: '操作/备注', key: 'action' },
  73 +]
  74 +
  75 +async function loadList() {
  76 + loading.value = true
  77 + try {
  78 + // 退款列表需要后端补充列表接口,暂用空列表
  79 + list.value = []
  80 + } finally { loading.value = false }
  81 +}
  82 +
  83 +async function handle(recordId: number, status: number, remark = '') {
  84 + await refundApi.handle(recordId, status, remark)
  85 + message.success('操作成功')
  86 + loadList()
  87 +}
  88 +
  89 +function openReject(record: any) {
  90 + currentRecordId.value = record.id
  91 + rejectRemark.value = ''
  92 + rejectVisible.value = true
  93 +}
  94 +
  95 +async function handleReject() {
  96 + saving.value = true
  97 + try {
  98 + await refundApi.handle(currentRecordId.value, 2, rejectRemark.value)
  99 + message.success('操作成功')
  100 + rejectVisible.value = false
  101 + loadList()
  102 + } finally { saving.value = false }
  103 +}
  104 +
  105 +onMounted(loadList)
  106 +</script>
... ...
src/views/rider/RiderEvaluateList.vue 0 → 100644
  1 +++ a/src/views/rider/RiderEvaluateList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="骑手评价" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-input-number v-model:value="filterRiderId" placeholder="骑手ID" style="width:130px" />
  7 + <a-select v-model:value="filterType" placeholder="评价类型" allowClear style="width:120px">
  8 + <a-select-option :value="0">全部</a-select-option>
  9 + <a-select-option :value="1">好评(4-5星)</a-select-option>
  10 + <a-select-option :value="2">中评(3星)</a-select-option>
  11 + <a-select-option :value="3">差评(1-2星)</a-select-option>
  12 + </a-select>
  13 + <a-button type="primary" @click="loadList">查询</a-button>
  14 + </a-space>
  15 + </template>
  16 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  17 + <template #bodyCell="{ column, record }">
  18 + <template v-if="column.key === 'star'">
  19 + <a-rate :value="record.star" disabled />
  20 + </template>
  21 + </template>
  22 + </a-table>
  23 + </a-card>
  24 + </div>
  25 +</template>
  26 +
  27 +<script setup lang="ts">
  28 +import { ref } from 'vue'
  29 +import { riderApi } from '@/api'
  30 +
  31 +const loading = ref(false)
  32 +const list = ref<any[]>([])
  33 +const filterRiderId = ref<number | undefined>()
  34 +const filterType = ref(0)
  35 +
  36 +const columns = [
  37 + { title: 'ID', dataIndex: 'id', width: 80 },
  38 + { title: '骑手ID', dataIndex: 'rid' },
  39 + { title: '评分', key: 'star' },
  40 + { title: '内容', dataIndex: 'content', ellipsis: true },
  41 + { title: '城市', dataIndex: 'cityId' },
  42 +]
  43 +
  44 +async function loadList() {
  45 + if (!filterRiderId.value) return
  46 + loading.value = true
  47 + try {
  48 + const res: any = await riderApi.evaluateList(filterRiderId.value, filterType.value)
  49 + list.value = res.data || []
  50 + } finally { loading.value = false }
  51 +}
  52 +</script>
... ...
src/views/rider/RiderList.vue 0 → 100644
  1 +++ a/src/views/rider/RiderList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="骑手管理" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-select v-model:value="filterStatus" placeholder="审核状态" allowClear style="width:120px" @change="loadList">
  7 + <a-select-option :value="2">待审核</a-select-option>
  8 + <a-select-option :value="1">已通过</a-select-option>
  9 + <a-select-option :value="0">已拒绝</a-select-option>
  10 + </a-select>
  11 + <a-input-search v-model:value="keyword" placeholder="搜索姓名/手机" @search="loadList" style="width:200px" />
  12 + <a-button type="primary" @click="openAdd">新增骑手</a-button>
  13 + </a-space>
  14 + </template>
  15 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  16 + <template #bodyCell="{ column, record }">
  17 + <template v-if="column.key === 'userStatus'">
  18 + <a-tag :color="record.userStatus === 1 ? 'green' : record.userStatus === 0 ? 'red' : 'orange'">
  19 + {{ statusMap[record.userStatus] }}
  20 + </a-tag>
  21 + </template>
  22 + <template v-if="column.key === 'levelName'">
  23 + {{ record.levelName || '默认等级' }}
  24 + </template>
  25 + <template v-if="column.key === 'accountStatus'">
  26 + <a-tag :color="getAccountStatus(record) === 1 ? 'green' : 'red'">
  27 + {{ getAccountStatus(record) === 1 ? '正常' : '禁用' }}
  28 + </a-tag>
  29 + </template>
  30 + <template v-if="column.key === 'type'">
  31 + <a-tag>{{ record.type === 1 ? '兼职' : '全职' }}</a-tag>
  32 + </template>
  33 + <template v-if="column.key === 'action'">
  34 + <a-space>
  35 + <a @click="openSetLevel(record)">设置等级</a>
  36 + <template v-if="record.userStatus === 2">
  37 + <a-popconfirm title="确认通过审核?" @confirm="setStatus(record.id, 1)">
  38 + <a style="color:green">通过</a>
  39 + </a-popconfirm>
  40 + <a-popconfirm title="确认拒绝?" @confirm="setStatus(record.id, 0)">
  41 + <a style="color:red">拒绝</a>
  42 + </a-popconfirm>
  43 + </template>
  44 + <template v-else>
  45 + <a-popconfirm
  46 + :title="getAccountStatus(record) === 1 ? '确认禁用该骑手?' : '确认启用该骑手?'"
  47 + @confirm="setEnableStatus(record.id, getAccountStatus(record) === 1 ? 0 : 1)">
  48 + <a :style="getAccountStatus(record) === 1 ? 'color:red' : ''">
  49 + {{ getAccountStatus(record) === 1 ? '禁用' : '启用' }}
  50 + </a>
  51 + </a-popconfirm>
  52 + <a-popconfirm
  53 + :title="record.type === 1 ? '切换为全职?' : '切换为兼职?'"
  54 + @confirm="setType(record.id, record.type === 1 ? 2 : 1)">
  55 + <a>切换{{ record.type === 1 ? '全职' : '兼职' }}</a>
  56 + </a-popconfirm>
  57 + </template>
  58 + </a-space>
  59 + </template>
  60 + </template>
  61 + </a-table>
  62 + </a-card>
  63 +
  64 + <a-modal v-model:open="modalVisible" title="新增骑手" @ok="handleAdd" :confirmLoading="saving">
  65 + <a-form :model="form" layout="vertical">
  66 + <a-form-item v-if="isAdmin" label="城市">
  67 + <a-select v-model:value="form.cityId" placeholder="选择城市">
  68 + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
  69 + </a-select>
  70 + </a-form-item>
  71 + <a-form-item label="昵称">
  72 + <a-input v-model:value="form.userNickname" placeholder="请输入骑手昵称" />
  73 + </a-form-item>
  74 + <a-form-item label="手机号">
  75 + <a-input v-model:value="form.mobile" placeholder="请输入手机号" />
  76 + </a-form-item>
  77 + <a-form-item label="密码">
  78 + <a-input-password v-model:value="form.password" placeholder="请输入登录密码" />
  79 + </a-form-item>
  80 + </a-form>
  81 + </a-modal>
  82 +
  83 + <a-modal v-model:open="levelVisible" title="设置骑手等级" @ok="handleSetLevel" :confirmLoading="levelSaving">
  84 + <a-form layout="vertical">
  85 + <a-form-item label="骑手">
  86 + <a-input :value="levelTargetName" disabled />
  87 + </a-form-item>
  88 + <a-form-item label="等级">
  89 + <a-select v-model:value="selectedLevelId" placeholder="请选择等级">
  90 + <a-select-option :value="0">使用默认等级</a-select-option>
  91 + <a-select-option v-for="item in levelOptions" :key="item.id" :value="item.id">
  92 + {{ item.name }}{{ item.isDefault === 1 ? '(默认)' : '' }}
  93 + </a-select-option>
  94 + </a-select>
  95 + </a-form-item>
  96 + </a-form>
  97 + </a-modal>
  98 + </div>
  99 +</template>
  100 +
  101 +<script setup lang="ts">
  102 +import { computed, reactive, ref, onMounted } from 'vue'
  103 +import { message } from 'ant-design-vue'
  104 +import { cityApi, riderApi, riderLevelApi } from '@/api'
  105 +import { useAuthStore } from '@/stores/auth'
  106 +
  107 +const auth = useAuthStore()
  108 +const loading = ref(false)
  109 +const saving = ref(false)
  110 +const list = ref<any[]>([])
  111 +const cityList = ref<any[]>([])
  112 +const levelOptions = ref<any[]>([])
  113 +const filterStatus = ref<number | undefined>()
  114 +const keyword = ref('')
  115 +const modalVisible = ref(false)
  116 +const levelVisible = ref(false)
  117 +const form = reactive({
  118 + cityId: undefined as number | undefined,
  119 + userNickname: '',
  120 + mobile: '',
  121 + password: '',
  122 +})
  123 +const levelSaving = ref(false)
  124 +const levelTargetId = ref<number>(0)
  125 +const levelTargetName = ref('')
  126 +const selectedLevelId = ref<number>(0)
  127 +const isAdmin = computed(() => auth.userInfo?.role === 'admin')
  128 +
  129 +const statusMap: Record<number, string> = { 0: '已拒绝', 1: '已通过', 2: '待审核' }
  130 +
  131 +const columns = [
  132 + { title: 'ID', dataIndex: 'id', width: 80 },
  133 + { title: '昵称', dataIndex: 'userNickname' },
  134 + { title: '手机', dataIndex: 'mobile' },
  135 + { title: '城市ID', dataIndex: 'cityId' },
  136 + { title: '等级', key: 'levelName' },
  137 + { title: '类型', key: 'type' },
  138 + { title: '审核状态', key: 'userStatus' },
  139 + { title: '账号状态', key: 'accountStatus' },
  140 + { title: '余额', dataIndex: 'balance' },
  141 + { title: '操作', key: 'action' },
  142 +]
  143 +
  144 +function getAccountStatus(record: any) {
  145 + return record.status === 0 ? 0 : 1
  146 +}
  147 +
  148 +async function loadList() {
  149 + loading.value = true
  150 + try {
  151 + const res: any = await riderApi.list({
  152 + keyword: keyword.value,
  153 + userStatus: filterStatus.value,
  154 + }).catch(() => ({ data: [] }))
  155 + list.value = Array.isArray(res?.data) ? res.data : []
  156 + } finally { loading.value = false }
  157 +}
  158 +
  159 +async function loadCities() {
  160 + const res: any = await cityApi.openList()
  161 + cityList.value = Array.isArray(res?.data) ? res.data : []
  162 +}
  163 +
  164 +function openAdd() {
  165 + Object.assign(form, {
  166 + cityId: undefined,
  167 + userNickname: '',
  168 + mobile: '',
  169 + password: '',
  170 + })
  171 + modalVisible.value = true
  172 +}
  173 +
  174 +async function handleAdd() {
  175 + if (!form.userNickname || !form.mobile || !form.password) {
  176 + message.error('请填写完整骑手信息')
  177 + return
  178 + }
  179 + if (isAdmin.value && !form.cityId) {
  180 + message.error('请选择城市')
  181 + return
  182 + }
  183 +
  184 + saving.value = true
  185 + try {
  186 + await riderApi.add({ ...form })
  187 + message.success('新增成功')
  188 + modalVisible.value = false
  189 + loadList()
  190 + } finally {
  191 + saving.value = false
  192 + }
  193 +}
  194 +
  195 +async function openSetLevel(record: any) {
  196 + const res: any = await riderLevelApi.list(record.cityId)
  197 + levelOptions.value = Array.isArray(res?.data) ? res.data : []
  198 + levelTargetId.value = record.id
  199 + levelTargetName.value = record.userNickname || record.mobile
  200 + selectedLevelId.value = record.levelId || 0
  201 + levelVisible.value = true
  202 +}
  203 +
  204 +async function handleSetLevel() {
  205 + levelSaving.value = true
  206 + try {
  207 + await riderApi.setLevel(levelTargetId.value, selectedLevelId.value || undefined)
  208 + message.success('设置成功')
  209 + levelVisible.value = false
  210 + loadList()
  211 + } finally {
  212 + levelSaving.value = false
  213 + }
  214 +}
  215 +
  216 +async function setStatus(riderId: number, status: number) {
  217 + await riderApi.setStatus(riderId, status)
  218 + message.success('操作成功')
  219 + loadList()
  220 +}
  221 +
  222 +async function setEnableStatus(riderId: number, status: number) {
  223 + await riderApi.setEnableStatus(riderId, status)
  224 + message.success('操作成功')
  225 + loadList()
  226 +}
  227 +
  228 +async function setType(riderId: number, type: number) {
  229 + await riderApi.setType(riderId, type)
  230 + message.success('操作成功')
  231 + loadList()
  232 +}
  233 +
  234 +onMounted(() => {
  235 + loadList()
  236 + if (isAdmin.value) {
  237 + loadCities()
  238 + }
  239 +})
  240 +</script>
... ...
src/views/substation/SubstationList.vue 0 → 100644
  1 +++ a/src/views/substation/SubstationList.vue
  1 +<template>
  2 + <div>
  3 + <a-card title="分站管理" :bordered="false">
  4 + <template #extra>
  5 + <a-space>
  6 + <a-input-search v-model:value="keyword" placeholder="搜索账号/昵称/手机" @search="loadList" style="width:220px" />
  7 + <a-button type="primary" @click="openAdd">新增分站</a-button>
  8 + </a-space>
  9 + </template>
  10 + <a-table :dataSource="list" :columns="columns" :loading="loading" rowKey="id" :pagination="false">
  11 + <template #bodyCell="{ column, record }">
  12 + <template v-if="column.key === 'status'">
  13 + <a-tag :color="record.userStatus === 1 ? 'green' : 'red'">
  14 + {{ record.userStatus === 1 ? '正常' : '禁用' }}
  15 + </a-tag>
  16 + </template>
  17 + <template v-if="column.key === 'action'">
  18 + <a-space>
  19 + <a @click="openEdit(record)">编辑</a>
  20 + <a @click="openChangePwd(record)">改密码</a>
  21 + <a-popconfirm
  22 + :title="record.userStatus === 1 ? '确认禁用?' : '确认启用?'"
  23 + @confirm="toggleBan(record)">
  24 + <a :style="record.userStatus === 1 ? 'color:red' : ''">
  25 + {{ record.userStatus === 1 ? '禁用' : '启用' }}
  26 + </a>
  27 + </a-popconfirm>
  28 + <a-popconfirm title="确认删除?" @confirm="handleDel(record.id)">
  29 + <a style="color:red">删除</a>
  30 + </a-popconfirm>
  31 + </a-space>
  32 + </template>
  33 + </template>
  34 + </a-table>
  35 + </a-card>
  36 +
  37 + <a-modal v-model:open="modalVisible" :title="editingId ? '编辑分站' : '新增分站'"
  38 + @ok="handleSave" :confirmLoading="saving">
  39 + <a-form :model="form" layout="vertical">
  40 + <a-form-item label="管理城市">
  41 + <a-select v-model:value="form.cityId" placeholder="选择城市">
  42 + <a-select-option v-for="c in cityList" :key="c.id" :value="c.id">{{ c.name }}</a-select-option>
  43 + </a-select>
  44 + </a-form-item>
  45 + <a-form-item label="登录账号">
  46 + <a-input v-model:value="form.userLogin" :disabled="!!editingId" />
  47 + </a-form-item>
  48 + <a-form-item label="昵称">
  49 + <a-input v-model:value="form.userNickname" />
  50 + </a-form-item>
  51 + <a-form-item label="手机号">
  52 + <a-input v-model:value="form.mobile" />
  53 + </a-form-item>
  54 + <a-form-item :label="editingId ? '新密码(不填不修改)' : '密码'">
  55 + <a-input-password v-model:value="form.userPass" />
  56 + </a-form-item>
  57 + </a-form>
  58 + </a-modal>
  59 +
  60 + <!-- 改密码弹窗 -->
  61 + <a-modal v-model:open="pwdVisible" title="修改密码" @ok="handleChangePwd" :confirmLoading="pwdSaving">
  62 + <a-form layout="vertical">
  63 + <a-form-item label="原密码">
  64 + <a-input-password v-model:value="pwdForm.oldPassword" />
  65 + </a-form-item>
  66 + <a-form-item label="新密码">
  67 + <a-input-password v-model:value="pwdForm.newPassword" />
  68 + </a-form-item>
  69 + </a-form>
  70 + </a-modal>
  71 + </div>
  72 +</template>
  73 +
  74 +<script setup lang="ts">
  75 +import { ref, reactive, onMounted } from 'vue'
  76 +import { message } from 'ant-design-vue'
  77 +import { substationApi, cityApi } from '@/api'
  78 +
  79 +const loading = ref(false)
  80 +const saving = ref(false)
  81 +const list = ref<any[]>([])
  82 +const cityList = ref<any[]>([])
  83 +const keyword = ref('')
  84 +const modalVisible = ref(false)
  85 +const editingId = ref<number | null>(null)
  86 +const form = reactive({ cityId: undefined, userLogin: '', userNickname: '', mobile: '', userPass: '' })
  87 +
  88 +const columns = [
  89 + { title: 'ID', dataIndex: 'id', width: 80 },
  90 + { title: '账号', dataIndex: 'userLogin' },
  91 + { title: '昵称', dataIndex: 'userNickname' },
  92 + { title: '手机', dataIndex: 'mobile' },
  93 + { title: '城市ID', dataIndex: 'cityId' },
  94 + { title: '状态', key: 'status' },
  95 + { title: '操作', key: 'action' },
  96 +]
  97 +
  98 +async function loadList() {
  99 + loading.value = true
  100 + try {
  101 + const res: any = await substationApi.list(keyword.value)
  102 + list.value = res.data
  103 + } finally { loading.value = false }
  104 +}
  105 +
  106 +async function loadCities() {
  107 + const res: any = await cityApi.openList()
  108 + cityList.value = res.data
  109 +}
  110 +
  111 +function openAdd() {
  112 + editingId.value = null
  113 + Object.assign(form, { cityId: undefined, userLogin: '', userNickname: '', mobile: '', userPass: '' })
  114 + modalVisible.value = true
  115 +}
  116 +
  117 +function openEdit(record: any) {
  118 + editingId.value = record.id
  119 + Object.assign(form, { cityId: record.cityId, userLogin: record.userLogin, userNickname: record.userNickname, mobile: record.mobile, userPass: '' })
  120 + modalVisible.value = true
  121 +}
  122 +
  123 +async function handleSave() {
  124 + saving.value = true
  125 + try {
  126 + if (editingId.value) {
  127 + await substationApi.edit({ ...form, id: editingId.value })
  128 + } else {
  129 + await substationApi.add(form)
  130 + }
  131 + message.success('保存成功')
  132 + modalVisible.value = false
  133 + loadList()
  134 + } finally { saving.value = false }
  135 +}
  136 +
  137 +async function toggleBan(record: any) {
  138 + if (record.userStatus === 1) {
  139 + await substationApi.ban(record.id)
  140 + } else {
  141 + await substationApi.cancelBan(record.id)
  142 + }
  143 + message.success('操作成功')
  144 + loadList()
  145 +}
  146 +
  147 +async function handleDel(id: number) {
  148 + await substationApi.del(id)
  149 + message.success('删除成功')
  150 + loadList()
  151 +}
  152 +
  153 +// 改密码
  154 +const pwdVisible = ref(false)
  155 +const pwdSaving = ref(false)
  156 +const pwdForm = reactive({ oldPassword: '', newPassword: '' })
  157 +const pwdTargetId = ref(0)
  158 +
  159 +function openChangePwd(record: any) {
  160 + pwdTargetId.value = record.id
  161 + Object.assign(pwdForm, { oldPassword: '', newPassword: '' })
  162 + pwdVisible.value = true
  163 +}
  164 +
  165 +async function handleChangePwd() {
  166 + if (!pwdForm.oldPassword || !pwdForm.newPassword) {
  167 + message.error('请填写完整密码')
  168 + return
  169 + }
  170 + pwdSaving.value = true
  171 + try {
  172 + await substationApi.changePassword(pwdForm)
  173 + message.success('密码修改成功')
  174 + pwdVisible.value = false
  175 + } finally { pwdSaving.value = false }
  176 +}
  177 +
  178 +onMounted(() => { loadList(); loadCities() })
  179 +</script>
... ...
tsconfig.app.json 0 → 100644
  1 +++ a/tsconfig.app.json
  1 +{
  2 + "extends": "@vue/tsconfig/tsconfig.dom.json",
  3 + "compilerOptions": {
  4 + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
  5 + "types": ["vite/client"],
  6 + "baseUrl": ".",
  7 + "paths": {
  8 + "@/*": ["src/*"]
  9 + },
  10 + "strict": true,
  11 + "noUnusedLocals": false,
  12 + "noUnusedParameters": false,
  13 + "erasableSyntaxOnly": true,
  14 + "noFallthroughCasesInSwitch": true,
  15 + "noUncheckedSideEffectImports": false
  16 + },
  17 + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
  18 +}
... ...
tsconfig.json 0 → 100644
  1 +++ a/tsconfig.json
  1 +{
  2 + "files": [],
  3 + "references": [
  4 + { "path": "./tsconfig.app.json" },
  5 + { "path": "./tsconfig.node.json" }
  6 + ]
  7 +}
... ...
tsconfig.node.json 0 → 100644
  1 +++ a/tsconfig.node.json
  1 +{
  2 + "compilerOptions": {
  3 + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
  4 + "target": "ES2023",
  5 + "lib": ["ES2023"],
  6 + "module": "ESNext",
  7 + "types": ["node"],
  8 + "skipLibCheck": true,
  9 +
  10 + /* Bundler mode */
  11 + "moduleResolution": "bundler",
  12 + "allowImportingTsExtensions": true,
  13 + "verbatimModuleSyntax": true,
  14 + "moduleDetection": "force",
  15 + "noEmit": true,
  16 +
  17 + /* Linting */
  18 + "strict": true,
  19 + "noUnusedLocals": true,
  20 + "noUnusedParameters": true,
  21 + "erasableSyntaxOnly": true,
  22 + "noFallthroughCasesInSwitch": true,
  23 + "noUncheckedSideEffectImports": true
  24 + },
  25 + "include": ["vite.config.ts"]
  26 +}
... ...
vite.config.ts 0 → 100644
  1 +++ a/vite.config.ts
  1 +import { defineConfig } from 'vite'
  2 +import vue from '@vitejs/plugin-vue'
  3 +import { resolve } from 'path'
  4 +
  5 +export default defineConfig({
  6 + plugins: [vue()],
  7 + resolve: {
  8 + alias: {
  9 + '@': resolve(__dirname, 'src'),
  10 + },
  11 + },
  12 + server: {
  13 + port: 3000,
  14 + proxy: {
  15 + '/api': {
  16 + target: 'http://10.30.110.149:8080',
  17 + changeOrigin: true,
  18 + },
  19 + },
  20 + },
  21 +})
... ...