gujiheimao před 1 rokem
rodič
revize
8935947f61

+ 24 - 0
ui/.gitignore

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

+ 12 - 0
ui/README.md

@@ -0,0 +1,12 @@
+1. 更新npm `npm install -g npm@10.2.2`
+2. 创建vite项目 `npm create vite@latest`
+3. 安装路由 `npm install vue-router@4`
+4. 安装sass `npm install -D sass`
+5. 安装@type/node `npm i --save-dev @types/node`
+6. 安装axios  `npm install axios`
+7. 安装elementUI `npm install element-plus --save`
+8. 安装elementUI Icon`npm install @element-plus/icons-vue`
+9. 安装VUEX`npm install vuex@next --save`
+10. npm install --save-dev @types/vuex
+10. 安装富文编辑器`npm install @wangeditor/editor @wangeditor/editor-for-vue`
+11. 编译提示内存不足`npm install --save-dev cross-env`

+ 13 - 0
ui/index.html

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

+ 1116 - 0
ui/package-lock.json

@@ -0,0 +1,1116 @@
+{
+  "name": "arkshoptools",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "arkshoptools",
+      "version": "0.0.0",
+      "dependencies": {
+        "@element-plus/icons-vue": "^2.3.1",
+        "axios": "^1.7.2",
+        "element-plus": "^2.7.8",
+        "vue": "^3.4.21",
+        "vue-router": "^4.4.0",
+        "vuex": "^4.0.2"
+      },
+      "devDependencies": {
+        "@vitejs/plugin-vue": "^5.0.4",
+        "sass": "^1.77.8",
+        "typescript": "^5.2.2",
+        "vite": "^5.2.0",
+        "vue-tsc": "^2.0.6"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.25.0",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.0.tgz",
+      "integrity": "sha512-CzdIU9jdP0dg7HdyB+bHvDJGagUv+qtzZt5rYCWwW6tITNqV9odjp6Qu41gkG0ca5UfdDUWrKkiAnHHdGRnOrA==",
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.6.1",
+      "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
+      "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@element-plus/icons-vue": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz",
+      "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==",
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@floating-ui/core": {
+      "version": "1.6.5",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.6.5.tgz",
+      "integrity": "sha512-8GrTWmoFhm5BsMZOTHeGD2/0FLKLQQHvO/ZmQga4tKempYRLz8aqJGqXVuQgisnMObq2YZ2SgkwctN1LOOxcqA==",
+      "dependencies": {
+        "@floating-ui/utils": "^0.2.5"
+      }
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.6.8",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.6.8.tgz",
+      "integrity": "sha512-kx62rP19VZ767Q653wsP1XZCGIirkE09E0QUGNYTM/ttbbQHqcGPdSfWFxUyyNLc/W6aoJRBajOSXhP6GXjC0Q==",
+      "dependencies": {
+        "@floating-ui/core": "^1.6.0",
+        "@floating-ui/utils": "^0.2.5"
+      }
+    },
+    "node_modules/@floating-ui/utils": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.5.tgz",
+      "integrity": "sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ=="
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+    },
+    "node_modules/@popperjs/core": {
+      "name": "@sxzz/popperjs-es",
+      "version": "2.11.7",
+      "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+      "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.19.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz",
+      "integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.17.7",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.7.tgz",
+      "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA=="
+    },
+    "node_modules/@types/lodash-es": {
+      "version": "4.17.12",
+      "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
+      "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/web-bluetooth": {
+      "version": "0.0.16",
+      "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
+      "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz",
+      "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==",
+      "dev": true,
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^5.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@volar/language-core": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==",
+      "dev": true,
+      "dependencies": {
+        "@volar/source-map": "2.4.0-alpha.18"
+      }
+    },
+    "node_modules/@volar/source-map": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==",
+      "dev": true
+    },
+    "node_modules/@volar/typescript": {
+      "version": "2.4.0-alpha.18",
+      "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.0-alpha.18.tgz",
+      "integrity": "sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==",
+      "dev": true,
+      "dependencies": {
+        "@volar/language-core": "2.4.0-alpha.18",
+        "path-browserify": "^1.0.1",
+        "vscode-uri": "^3.0.8"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz",
+      "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==",
+      "dependencies": {
+        "@babel/parser": "^7.24.4",
+        "@vue/shared": "3.4.27",
+        "entities": "^4.5.0",
+        "estree-walker": "^2.0.2",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz",
+      "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==",
+      "dependencies": {
+        "@vue/compiler-core": "3.4.27",
+        "@vue/shared": "3.4.27"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz",
+      "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==",
+      "dependencies": {
+        "@babel/parser": "^7.24.4",
+        "@vue/compiler-core": "3.4.27",
+        "@vue/compiler-dom": "3.4.27",
+        "@vue/compiler-ssr": "3.4.27",
+        "@vue/shared": "3.4.27",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.30.10",
+        "postcss": "^8.4.38",
+        "source-map-js": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz",
+      "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.27",
+        "@vue/shared": "3.4.27"
+      }
+    },
+    "node_modules/@vue/compiler-vue2": {
+      "version": "2.7.16",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+      "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
+      "dev": true,
+      "dependencies": {
+        "de-indent": "^1.0.2",
+        "he": "^1.2.0"
+      }
+    },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.6.3",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.3.tgz",
+      "integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw=="
+    },
+    "node_modules/@vue/language-core": {
+      "version": "2.0.29",
+      "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.29.tgz",
+      "integrity": "sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==",
+      "dev": true,
+      "dependencies": {
+        "@volar/language-core": "~2.4.0-alpha.18",
+        "@vue/compiler-dom": "^3.4.0",
+        "@vue/compiler-vue2": "^2.7.16",
+        "@vue/shared": "^3.4.0",
+        "computeds": "^0.0.1",
+        "minimatch": "^9.0.3",
+        "muggle-string": "^0.4.1",
+        "path-browserify": "^1.0.1"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz",
+      "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==",
+      "dependencies": {
+        "@vue/shared": "3.4.27"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz",
+      "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==",
+      "dependencies": {
+        "@vue/reactivity": "3.4.27",
+        "@vue/shared": "3.4.27"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz",
+      "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==",
+      "dependencies": {
+        "@vue/runtime-core": "3.4.27",
+        "@vue/shared": "3.4.27",
+        "csstype": "^3.1.3"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz",
+      "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.4.27",
+        "@vue/shared": "3.4.27"
+      },
+      "peerDependencies": {
+        "vue": "3.4.27"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz",
+      "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA=="
+    },
+    "node_modules/@vueuse/core": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
+      "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.16",
+        "@vueuse/metadata": "9.13.0",
+        "@vueuse/shared": "9.13.0",
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/core/node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
+      "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
+      "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
+      "dependencies": {
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@vueuse/shared/node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/async-validator": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.2.tgz",
+      "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/computeds": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz",
+      "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==",
+      "dev": true
+    },
+    "node_modules/csstype": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+      "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.12",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.12.tgz",
+      "integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg=="
+    },
+    "node_modules/de-indent": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+      "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+      "dev": true
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/element-plus": {
+      "version": "2.7.8",
+      "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.7.8.tgz",
+      "integrity": "sha512-h6dx2XihAbQaud0v+6O7Fy0b0G3YNplNVH7QnK3csTcvQd4y4raiyMRQpf9EKbRbTMdNrFsqAZrs9ok9DMcJHg==",
+      "dependencies": {
+        "@ctrl/tinycolor": "^3.4.1",
+        "@element-plus/icons-vue": "^2.3.1",
+        "@floating-ui/dom": "^1.0.1",
+        "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+        "@types/lodash": "^4.14.182",
+        "@types/lodash-es": "^4.17.6",
+        "@vueuse/core": "^9.1.0",
+        "async-validator": "^4.2.5",
+        "dayjs": "^1.11.3",
+        "escape-html": "^1.0.3",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "lodash-unified": "^1.0.2",
+        "memoize-one": "^6.0.0",
+        "normalize-wheel-es": "^1.2.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.6",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+      "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.3.7.tgz",
+      "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+    },
+    "node_modules/lodash-unified": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
+      "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==",
+      "peerDependencies": {
+        "@types/lodash-es": "*",
+        "lodash": "*",
+        "lodash-es": "*"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.11",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
+      "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/muggle-string": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+      "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+      "dev": true
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.7",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+      "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/normalize-wheel-es": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+      "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
+    },
+    "node_modules/path-browserify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+      "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+      "dev": true
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
+      "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.4.40",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.40.tgz",
+      "integrity": "sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.7",
+        "picocolors": "^1.0.1",
+        "source-map-js": "^1.2.0"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.19.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz",
+      "integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "1.0.5"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.19.1",
+        "@rollup/rollup-android-arm64": "4.19.1",
+        "@rollup/rollup-darwin-arm64": "4.19.1",
+        "@rollup/rollup-darwin-x64": "4.19.1",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.19.1",
+        "@rollup/rollup-linux-arm-musleabihf": "4.19.1",
+        "@rollup/rollup-linux-arm64-gnu": "4.19.1",
+        "@rollup/rollup-linux-arm64-musl": "4.19.1",
+        "@rollup/rollup-linux-powerpc64le-gnu": "4.19.1",
+        "@rollup/rollup-linux-riscv64-gnu": "4.19.1",
+        "@rollup/rollup-linux-s390x-gnu": "4.19.1",
+        "@rollup/rollup-linux-x64-gnu": "4.19.1",
+        "@rollup/rollup-linux-x64-musl": "4.19.1",
+        "@rollup/rollup-win32-arm64-msvc": "4.19.1",
+        "@rollup/rollup-win32-ia32-msvc": "4.19.1",
+        "@rollup/rollup-win32-x64-msvc": "4.19.1",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/sass": {
+      "version": "1.77.8",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.77.8.tgz",
+      "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/semver": {
+      "version": "7.6.3",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+      "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
+      "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
+      "devOptional": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/vite": {
+      "version": "5.3.5",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.5.tgz",
+      "integrity": "sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.39",
+        "rollup": "^4.13.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vscode-uri": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
+      "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==",
+      "dev": true
+    },
+    "node_modules/vue": {
+      "version": "3.4.27",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz",
+      "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.4.27",
+        "@vue/compiler-sfc": "3.4.27",
+        "@vue/runtime-dom": "3.4.27",
+        "@vue/server-renderer": "3.4.27",
+        "@vue/shared": "3.4.27"
+      },
+      "peerDependencies": {
+        "typescript": "*"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue-router": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.4.0.tgz",
+      "integrity": "sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.5.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/posva"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/vue-tsc": {
+      "version": "2.0.29",
+      "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.29.tgz",
+      "integrity": "sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==",
+      "dev": true,
+      "dependencies": {
+        "@volar/typescript": "~2.4.0-alpha.18",
+        "@vue/language-core": "2.0.29",
+        "semver": "^7.5.4"
+      },
+      "bin": {
+        "vue-tsc": "bin/vue-tsc.js"
+      },
+      "peerDependencies": {
+        "typescript": ">=5.0.0"
+      }
+    },
+    "node_modules/vuex": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
+      "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.0.0-beta.11"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.2"
+      }
+    }
+  }
+}

+ 26 - 0
ui/package.json

@@ -0,0 +1,26 @@
+{
+  "name": "arkshoptools",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vue-tsc && vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@element-plus/icons-vue": "^2.3.1",
+    "axios": "^1.7.2",
+    "element-plus": "^2.7.8",
+    "vue": "^3.4.21",
+    "vue-router": "^4.4.0",
+    "vuex": "^4.0.2"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^5.0.4",
+    "sass": "^1.77.8",
+    "typescript": "^5.2.2",
+    "vite": "^5.2.0",
+    "vue-tsc": "^2.0.6"
+  }
+}

binární
ui/public/logo.jpg


+ 11 - 0
ui/src/App.vue

@@ -0,0 +1,11 @@
+<template>
+  <router-view/>
+</template>
+<script setup lang="ts">
+import store from "./state/ItemList";
+
+store.commit("updateItemList");
+</script>
+<style scoped lang="scss">
+
+</style>

+ 104 - 0
ui/src/api/ARKItemsApi.ts

@@ -0,0 +1,104 @@
+import {GetDataByPath, PostDataByPath, PutDataByPath, ResponseData} from "./api";
+
+export class ArkInfo {
+    id: number
+    name: string
+    label: string
+    imgUrl: string
+    category: string
+    blueprint: string
+
+    constructor(id: number, name: string, label: string, imgUrl: string, category: string, blueprint: string) {
+        this.id = id;
+        this.name = name;
+        this.label = label;
+        this.imgUrl = imgUrl;
+        this.category = category;
+        this.blueprint = blueprint;
+    }
+}
+
+export class ArkItemInfo extends ArkInfo {
+
+    stackSize: number
+    itemId: string
+    className: string
+
+    constructor(id: number, name: string, label: string, imgUrl: string, category: string, stackSize: number, itemId: string, className: string, blueprint: string) {
+        super(id, name, label, imgUrl, category, blueprint);
+        this.stackSize = stackSize;
+        this.itemId = itemId;
+        this.className = className;
+    }
+}
+
+export class ArkDinoInfo extends ArkInfo {
+    nameTag: string
+    entityId: string
+
+    constructor(id: number, name: string, label: string, imgUrl: string, category: string, nameTag: string, entityId: string, blueprint: string) {
+        super(id, name, label, imgUrl, category, blueprint);
+        this.nameTag = nameTag;
+        this.entityId = entityId;
+    }
+}
+
+export class ArkItemData<T> {
+    list: T[]
+    total: number
+    pageNum: number
+    pageSize: number
+
+    constructor(list: T[], total: number, pageNum: number, pageSize: number) {
+        this.list = list;
+        this.total = total;
+        this.pageNum = pageNum;
+        this.pageSize = pageSize;
+    }
+}
+
+export function GetItemsApi() {
+    return GetDataByPath("/ark/arkItems", {}, false) as Promise<ResponseData<ArkItemData<ArkItemInfo>>>
+}
+
+export function GetDinosApi() {
+    return GetDataByPath("/ark/arkDinos", {}, false) as Promise<ResponseData<ArkItemData<ArkDinoInfo>>>
+}
+
+export class ReqArkItem {
+    pageNum: number
+    pageSize: number
+    name: string
+    category: string
+    label: string
+    allLike: string
+
+    constructor(pageNum: number, pageSize: number, name: string, category: string, label: string, allLike: string) {
+        this.pageNum = pageNum;
+        this.pageSize = pageSize;
+        this.name = name;
+        this.category = category;
+        this.label = label;
+        this.allLike = allLike;
+    }
+}
+
+export function PostItemList(item: ReqArkItem, pageNum: number, pageSize: number) {
+    item.pageNum = pageNum;
+    item.pageSize = pageSize;
+    return PostDataByPath("/ark/arkItems", item, false) as Promise<ResponseData<ArkItemData<ArkItemInfo>>>
+}
+
+export function PostDinoList(item: ReqArkItem, pageNum: number, pageSize: number) {
+    item.pageNum = pageNum;
+    item.pageSize = pageSize;
+    return PostDataByPath("/ark/arkDinos", item, false) as Promise<ResponseData<ArkItemData<ArkDinoInfo>>>
+}
+
+export function updateItemList(item: ArkItemInfo) {
+    return PutDataByPath("/ark/arkItem", item, false) as Promise<ResponseData<ArkItemInfo>>
+}
+
+export function updateDinoList(dino: ArkDinoInfo) {
+    return PutDataByPath("/ark/arkItem", dino, false) as Promise<ResponseData<ArkItemData<ArkDinoInfo>>>
+}

+ 148 - 0
ui/src/api/ARKShopItemAPI.ts

@@ -0,0 +1,148 @@
+import {Item} from "./ConfigFile";
+import {DeleteDataByPath, GetDataByPath, PostDataByPath, PutDataByPath, ResponseData} from "./api";
+
+export class ShopData {
+    Description: string
+    Price: number
+    Type: string
+
+    constructor(Description: string, Price: number, Type: string) {
+        this.Description = Description;
+        this.Price = Price;
+        this.Type = Type;
+    }
+}
+
+export class ShopItem extends ShopData {
+    Items: Item[]
+
+    static Create(): ShopItem {
+        return new ShopItem("item", "", 0, []);
+    }
+
+    constructor(Type: string, Description: string, Price: number, Items: Item[]) {
+        super(Description, Price, Type);
+        this.Items = Items;
+    }
+}
+
+export class ShopDino extends ShopData {
+    Level: number
+    MinLevel: number
+    MaxLevel: number
+    Neutered: boolean
+    Gender: string
+    SaddleBlueprint: string
+    Blueprint: string
+
+    static Create(): ShopDino {
+        return new ShopDino("", 0, "dino", 0, 0, 0, false, "", "", "");
+    }
+
+    constructor(Description: string, Price: number, Type: string, Level: number, MinLevel: number, MaxLevel: number, Neutered: boolean, Gender: string, SaddleBlueprint: string, Blueprint: string) {
+        super(Description, Price, Type);
+        this.Level = Level;
+        this.MinLevel = MinLevel;
+        this.MaxLevel = MaxLevel;
+        this.Neutered = Neutered;
+        this.Gender = Gender;
+        this.SaddleBlueprint = SaddleBlueprint;
+        this.Blueprint = Blueprint;
+    }
+}
+
+export class ShopBeacon extends ShopData {
+    ClassName: string
+
+    static Create(): ShopBeacon {
+        return new ShopBeacon("", 0, "beacon", "");
+    }
+    constructor(Description: string, Price: number, Type: string, ClassName: string) {
+        super(Description, Price, Type);
+        this.ClassName = ClassName;
+    }
+
+}
+
+//ShopExperience
+export class ShopExperience extends ShopData {
+    Amount: number
+    GiveToDino: boolean
+    static Create(): ShopExperience {
+        return new ShopExperience("", 0, "experience", 0, false);
+    }
+
+    constructor(Description: string, Price: number, Type: string, Amount: number, GiveToDino: boolean) {
+        super(Description, Price, Type);
+        this.Amount = Amount;
+        this.GiveToDino = GiveToDino;
+    }
+}
+
+//ShopUnlockengram
+export class ShopUnlockengram extends ShopData {
+    Items: ShopUnlockengramItem[]
+    static Create(): ShopUnlockengram {
+        return new ShopUnlockengram("", 0, "unlockengram", []);
+    }
+
+    constructor(Description: string, Price: number, Type: string, Items: ShopUnlockengramItem[]) {
+        super(Description, Price, Type);
+        this.Items = Items;
+    }
+}
+
+export class ShopUnlockengramItem {
+    Blueprint: string
+
+    static Create(): ShopUnlockengramItem {
+        return new ShopUnlockengramItem("");
+    }
+    constructor(Blueprint: string) {
+        this.Blueprint = Blueprint;
+    }
+}
+
+export class ShopCommand extends ShopData {
+    Items: ShopCommandItem[]
+
+    static Create(): ShopCommand {
+        return new ShopCommand("", 0, "command", []);
+    }
+    constructor(Description: string, Price: number, Type: string, Items: ShopCommandItem[]) {
+        super(Description, Price, Type);
+        this.Items = Items;
+    }
+}
+
+export class ShopCommandItem {
+    Command: string
+    DisplayAs: string
+
+    constructor(Command: string, DisplayAs: string) {
+        this.Command = Command;
+        this.DisplayAs = DisplayAs;
+    }
+}
+
+export function GetShopItems(type: string) {
+    return GetDataByPath("/ark/shopItem?type=" + type, {}, false) as Promise<ResponseData<ShopData[]>>;
+}
+
+export function SaveShopItem(shopItem: ShopData, name: string, shopType: string) {
+    return PostDataByPath("/ark/shopItem",
+        {shopItem: shopItem, shopItemName: name, shopType: shopType},
+        false)
+}
+
+export function UpdateShopItem(newShopItem: ShopData, name: string, shopType: string) {
+    return PutDataByPath("/ark/shopItem", {
+        newShopItem: newShopItem,
+        shopItemName: name, shopType: shopType
+    }, false)
+}
+
+export function DeleteShopItem(shopItemName: string) {
+    return DeleteDataByPath("/ark/shopItem", {shopItemName: shopItemName}, false)
+}
+

+ 23 - 0
ui/src/api/Login.ts

@@ -0,0 +1,23 @@
+import {GetDataByPath, PostDataByPath} from "./api";
+
+export class LoginInfo {
+    username: string = "";
+    password: string = "";
+
+    static Create() {
+        return new LoginInfo("", "");
+    }
+
+    constructor(username: string, password: string) {
+        this.username = username;
+        this.password = password;
+    }
+}
+
+export function arkLogin(info: LoginInfo) {
+    return PostDataByPath("/login", info);
+}
+
+export function ReplayConfig() {
+    return GetDataByPath("/replay");
+}

+ 137 - 0
ui/src/api/api.ts

@@ -0,0 +1,137 @@
+import axios, {AxiosHeaders} from "axios";
+import Router from "../router";
+
+
+export default {
+    GetDataByPath: GetDataByPath,
+    PostDataByPath: PostDataByPath,
+    PutDataByPath: PutDataByPath,
+    DeleteDataByPath: DeleteDataByPath,
+}
+
+function getToken(): string {
+    let token = localStorage.getItem("token");
+    if (!token) {
+        Router.push({path: "/"})
+        return ""
+    }
+    return token ?? "";
+}
+
+const HeaderTokenName = "auth-sign"
+const BaseApiPathPrefix = "/api/"
+const JsonGetTypeKey = "Accept"
+const JsonPostTypeKey = "Content-Type"
+
+export class ResponseData<T> {
+    data: T;
+    code: number;
+    message: string;
+
+    constructor(data: T, code: number, message: string) {
+        this.data = data;
+        this.code = code;
+        this.message = message;
+    }
+}
+
+export async function GetDataByPath(path: string, params?: Object, calibration = false): Promise<ResponseData<any>> {
+    let headers = new AxiosHeaders();
+    headers.set(JsonGetTypeKey, 'application/json',)
+    if (calibration) {
+        let token = getToken()
+        if (token === null || token === "") {
+            throw "账号未登录"
+        }
+        headers.set(HeaderTokenName, getToken())
+    }
+    let data = {}
+    await axios.get<ResponseData<any>>(BaseApiPathPrefix + path, {
+        headers: headers,
+        params: params
+    }).then(
+        res => {
+            if (res.data.code === 565) {
+                localStorage.removeItem("token")
+            }
+            data = res.data
+        }
+    )
+    return data as ResponseData<any>
+}
+
+export async function PostDataByPath(path: string, json?: Object, calibration = false): Promise<ResponseData<any>> {
+    let headers = new AxiosHeaders();
+    headers.set(JsonPostTypeKey, 'application/json',)
+    if (calibration) {
+        let token = getToken()
+        if (token === null || token === "") {
+            throw "账号未登录"
+        }
+        headers.set(HeaderTokenName, getToken())
+    }
+
+    let data = {}
+    await axios.post<ResponseData<any>>(BaseApiPathPrefix + path, json, {
+        headers: headers
+    }).then(
+        res => {
+            if (res.data.code === 565) {
+                localStorage.removeItem("token")
+            }
+            data = res.data
+        }
+    )
+    return data as ResponseData<any>
+}
+
+export async function PutDataByPath(path: string, json?: Object, calibration = false): Promise<ResponseData<any>> {
+    let headers = new AxiosHeaders();
+    headers.set(JsonPostTypeKey, 'application/json',)
+    if (calibration) {
+        let token = getToken()
+        if (token === null || token === "") {
+            throw "账号未登录"
+        }
+        headers.set(HeaderTokenName, getToken())
+    }
+
+    let data = {}
+    await axios.put<ResponseData<any>>(BaseApiPathPrefix + path, json, {
+        headers: headers
+    }).then(
+        res => {
+            if (res.data.code === 565) {
+                localStorage.removeItem("token")
+            }
+            data = res.data
+        }
+    )
+    return data as ResponseData<any>
+}
+
+
+export async function DeleteDataByPath(path: string, params?: Object, calibration = false): Promise<ResponseData<any>> {
+    let headers = new AxiosHeaders();
+    headers.set(JsonGetTypeKey, 'application/json',)
+    if (calibration) {
+        let token = getToken()
+        if (token === null || token === "") {
+            throw "账号未登录"
+        }
+        headers.set(HeaderTokenName, getToken())
+    }
+    let data = {}
+    await axios.delete<ResponseData<any>>(BaseApiPathPrefix + path, {
+        headers: headers,
+        params: params
+    }).then(
+        res => {
+            if (res.data.code === 565) {
+                localStorage.removeItem("token")
+            }
+            data = res.data
+        }
+    )
+    return data as ResponseData<any>
+}

binární
ui/src/assets/logo.jpg


+ 48 - 0
ui/src/components/ImageNameButton/ImageNameButton.vue

@@ -0,0 +1,48 @@
+<template>
+  <el-button
+      type="info"
+      @click="handleClick"
+      style="height: auto; width: auto"
+  >
+    <el-card
+        style="background-color: transparent; border: transparent; display: flex; width: 90px; height: 114px; padding: 0"
+    >
+      <el-image :src="imageSrc" style="width: 50px; height: 50px"></el-image>
+      <br /><br />
+      <el-text type="danger">{{ text }}</el-text>
+    </el-card>
+  </el-button>
+</template>
+
+<script>
+export default {
+  name: 'ImageNameButton',
+  props: {
+    // 默认图片路径
+    imageSrc: {
+      type: String,
+      default: '/api/static/image/Rex.png'
+    },
+    // 默认显示文本
+    text: {
+      type: String,
+      default: '恐龙信息'
+    },
+    // 点击事件的回调函数
+    onClick: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  methods: {
+    // 处理按钮点击事件
+    handleClick() {
+      this.onClick(); // 调用外部传入的回调函数
+    }
+  }
+}
+</script>
+
+<style scoped>
+/* 可以根据需要添加自定义样式 */
+</style>

+ 66 - 0
ui/src/components/item/ArkDinoSelect.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-select
+      v-model="value"
+      filterable
+      placeholder="Select"
+      style="width: 240px">
+    <el-option
+        v-for="item in dinoList"
+        :key="getData(item,props.keyType)"
+        :label="getData(item,props.labelType)"
+        :value="getData(item,props.valueType)"
+    />
+  </el-select>
+</template>
+<script setup lang="ts">
+import {ref} from "vue";
+import {ArkDinoInfo} from "../../api/ARKItemsApi";
+import store from "../../state/ItemList";
+
+const props = defineProps({
+  keyType: {
+    type: String,
+    default: 'blueprint'
+  },
+  valueType: {
+    type: String,
+    default: 'blueprint'
+  },
+  labelType: {
+    type: String,
+    default: 'label'
+  }
+})
+
+let dinoList = ref<ArkDinoInfo[]>([]);
+let value = defineModel("value")
+
+function getData(item: ArkDinoInfo, type: string) {
+  switch (type) {
+    case 'blueprint':
+      return item.blueprint;
+    case 'entityId':
+      return item.entityId;
+    case 'nameTag':
+      return item.nameTag;
+    case 'name':
+      return item.name;
+    case 'label':
+      return item.label;
+    case "category":
+      return item.category;
+    default:
+      return item.blueprint;
+  }
+}
+
+function replay() {
+  dinoList.value = store.state.dinoList;
+}
+
+replay()
+
+</script>
+<style scoped lang="scss">
+
+</style>

+ 67 - 0
ui/src/components/item/ArkItemSelect.vue

@@ -0,0 +1,67 @@
+<template>
+  <el-select
+      v-model="value"
+      filterable
+      placeholder="Select"
+      style="width: 240px">
+    <el-option
+        v-for="item in itemList"
+        :key="getData(item,props.keyType)"
+        :label="getData(item,props.labelType)"
+        :value="getData(item,props.valueType)"
+    />
+  </el-select>
+
+</template>
+<script setup lang="ts">
+import {ref} from "vue";
+import {ArkItemInfo} from "../../api/ARKItemsApi";
+import store from "../../state/ItemList";
+
+const props = defineProps({
+  keyType: {
+    type: String,
+    default: 'blueprint'
+  },
+  valueType: {
+    type: String,
+    default: 'blueprint'
+  },
+  labelType: {
+    type: String,
+    default: 'label'
+  }
+})
+
+let itemList = ref<ArkItemInfo[]>([]);
+let value = defineModel("value")
+
+function getData(item: ArkItemInfo, type: string) {
+  switch (type) {
+    case 'blueprint':
+      return item.blueprint;
+    case 'className':
+      return item.className;
+    case 'itemId':
+      return item.itemId;
+    case 'name':
+      return item.name;
+    case 'label':
+      return item.label;
+    case "category":
+      return item.category;
+    default:
+      return item.blueprint;
+  }
+}
+
+function replay() {
+  itemList.value = store.state.itemList;
+}
+
+replay()
+
+</script>
+<style scoped lang="scss">
+
+</style>

+ 69 - 0
ui/src/components/item/ArkItemText.vue

@@ -0,0 +1,69 @@
+<template>
+  <el-text :type="type">
+    {{ getValueText() }}
+  </el-text>
+</template>
+<script setup lang="ts">
+import {ref} from "vue";
+import store from "../../state/ItemList";
+import {ArkInfo} from "../../api/ARKItemsApi";
+
+const props = defineProps({
+  sourceType: {
+    type: String,
+    default: 'blueprint'
+  },
+  targetType: {
+    type: String,
+    default: 'label'
+  },
+  value: {
+    type: String,
+    default: ''
+  },
+  type: {
+    type: String,
+    default: 'info'
+  }
+})
+
+let ItemMap = ref<Map<string, ArkInfo>>(new Map<string, ArkInfo>());
+
+function getMapByType() {
+  if (props.sourceType === 'blueprint') {
+    ItemMap.value = store.state.ItemMap.blueprintMap;
+  }
+  if (props.sourceType === 'label') {
+    ItemMap.value = store.state.ItemMap.labelMap;
+  }
+  if (props.sourceType === 'name') {
+    ItemMap.value = store.state.ItemMap.nameMap;
+  }
+  if (props.sourceType === 'category') {
+    ItemMap.value = store.state.ItemMap.categoryMap;
+  }
+}
+
+getMapByType()
+
+function getValueText() {
+  let item = ItemMap.value.get(props.value);
+  if (!item) return '';
+  switch (props.targetType) {
+    case 'blueprint':
+      return item.blueprint;
+    case 'name':
+      return item.name;
+    case 'label':
+      return item.label;
+    case "category":
+      return item.category;
+  }
+}
+
+
+</script>
+<style scoped lang="scss">
+
+</style>
+

+ 19 - 0
ui/src/main.ts

@@ -0,0 +1,19 @@
+import {createApp} from 'vue'
+// @ts-ignore
+import Vuex from 'vuex'
+import App from './App.vue'
+import './style.css'
+import axios from 'axios'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import router from "./router";
+import store from "./state/ItemList";
+
+const app = createApp(App);
+
+app.use(ElementPlus)
+app.use(store);
+app.use(router)
+app.use(Vuex)
+app.provide('$axios', axios)
+app.mount('#app')

+ 67 - 0
ui/src/page/Login.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="body">
+    <el-card class="login-card">
+      <div class="login-card-logo">
+        <img class="login-card-logo" src="../assets/logo.jpg" alt="logo"/>
+      </div>
+      <el-form :model="loginForm" ref="loginFormRef" label-width="60px" size="large">
+        <el-form-item prop="username" label="用户名">
+          <el-input :prefix-icon="UserFilled" v-model="loginForm.username" placeholder="用户名"></el-input>
+        </el-form-item>
+        <el-form-item prop="password" label="密码">
+          <el-input :prefix-icon="Lock" type="password" v-model="loginForm.password" show-password placeholder="密码"/>
+        </el-form-item>
+        <div class="dialog-footer" style="display: flex;justify-content: center">
+          <el-button type="primary" @click="onSubmit">登录</el-button>
+        </div>
+      </el-form>
+
+    </el-card>
+  </div>
+</template>
+<script setup lang="ts">
+import {Lock, UserFilled} from "@element-plus/icons-vue";
+
+
+// let loginForm = reactive(LoginInfo.Create())
+// //初始化检查,是否已经登录
+// if (localStorage.getItem("token")) {
+//   router.push({path:"/back"})
+// }
+//
+// function onSubmit() {
+//   arkLogin(loginForm).then(res => {
+//     if (res.code == 200) {
+//       localStorage.setItem("token", res.data.token)
+//       router.push({path:"/back"})
+//     }
+//   })
+// }
+
+</script>
+<style scoped>
+.body {
+  width: 100VW;
+  height: 100VH;
+  display: flex;
+  justify-content: center;
+  flex-direction: column;
+  transform: translateY(-100px);
+
+  .login-card {
+    width: 380px;
+    height: 300px;
+    margin: 0 auto;
+
+    .login-card-logo {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+
+      img {
+        margin: 8px 0 25px;
+      }
+    }
+  }
+}
+</style>

+ 162 - 0
ui/src/page/back/ARKDinoListView.vue

@@ -0,0 +1,162 @@
+<template>
+  <div style="margin: 10px;padding: 10px">
+    <!--    <ArkItemSelect v-model="demo"/>-->
+    <el-form :model="searchForm" label-width="80px">
+      <el-row>
+        <el-col :span="4">
+          <el-form-item label="名称">
+            <el-input v-model="searchForm.name"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="标签">
+            <el-input v-model="searchForm.label"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="全部">
+            <el-input v-model="searchForm.allLike"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="分类">
+            <el-select>
+              <el-option :key="'Ammunition'" :value="'Ammunition'" :label="'弹药(Ammunition)'"></el-option>
+              <el-option :key="'Armor'" :value="'Armor'" :label="'护甲(Armor)'"></el-option>
+              <el-option :key="'Artifacts'" :value="'Artifacts'" :label="'遗物(Artifacts)'"></el-option>
+              <el-option :key="'Attachments'" :value="'Attachments'" :label="'附件(Attachments)'"></el-option>
+              <el-option :key="'Chibi-pets'" :value="'Chibi-pets'" :label="'小宠物(Chibi-pets)'"></el-option>
+              <el-option :key="'Consumables'" :value="'Consumables'" :label="'消耗品(Consumables)'"></el-option>
+              <el-option :key="'Dye'" :value="'Dye'" :label="'染料(Dye)'"></el-option>
+              <el-option :key="'Eggs'" :value="'Eggs'" :label="'蛋(Eggs)'"></el-option>
+              <el-option :key="'Farming'" :value="'Farming'" :label="'农业(Farming)'"></el-option>
+              <el-option :key="'Recipes'" :value="'Recipes'" :label="'配方(Recipes)'"></el-option>
+              <el-option :key="'Resources'" :value="'Resources'" :label="'资源(Resources)'"></el-option>
+              <el-option :key="'Saddles'" :value="'Saddles'" :label="'鞍(Saddles)'"></el-option>
+              <el-option :key="'Seeds'" :value="'Seeds'" :label="'种子(Seeds)'"></el-option>
+              <el-option :key="'Skins'" :value="'Skins'" :label="'皮肤(Skins)'"></el-option>
+              <el-option :key="'Structures'" :value="'Structures'" :label="'结构(Structures)'"></el-option>
+              <el-option :key="'Tools'" :value="'Tools'" :label="'工具(Tools)'"></el-option>
+              <el-option :key="'Trophies'" :value="'Trophies'" :label="'奖杯(Trophies)'"></el-option>
+              <el-option :key="'Weapons'" :value="'Weapons'" :label="'武器(Weapons)'"></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="1"></el-col>
+        <el-col :span="1.5">
+          <el-button class="el-btn-margin" type="primary" @click="replay()">查询</el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button class="el-btn-margin" type="primary" @click="replay()">重置</el-button>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <el-table :data="items" style="width: 100%">
+      <el-table-column prop="id" label="ID" width="100"></el-table-column>
+      <el-table-column prop="name" label="名称"></el-table-column>
+      <el-table-column prop="label" label="标签"></el-table-column>
+      <el-table-column prop="imgUrl" label="图片">
+        <template #default="scope">
+          <el-image :src="GetImg(scope.row.imgUrl)" loading="lazy" style="max-width: 45px;max-height: 45px"></el-image>
+        </template>
+      </el-table-column>
+      <el-table-column prop="nameTag" label="类名"></el-table-column>
+      <el-table-column prop="category" label="分类"></el-table-column>
+      <el-table-column prop="blueprint" label="功能" fixed="right">
+        <template #default="scope">
+          <el-link type="info">复制蓝图</el-link>&nbsp;
+          <el-link type="primary">复制代码</el-link>&nbsp;
+          <el-link type="primary">详情</el-link>
+        </template>
+      </el-table-column>
+      <el-table-column prop="blueprint" label="操作" fixed="right">
+        <template #default="scope">
+          <el-button link type="info" @click="open=true">修改</el-button>
+          <el-button linktype="primary">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-pagination
+        v-model:current-page="pageNum"
+        v-model:page-size="pageSize"
+        :page-sizes="[10, 20, 50, 100]"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"/>
+
+    <el-dialog v-model="open">
+      <el-form :model="arkDinoForm" label-width="80px">
+        <el-form-item label="名称">
+          <el-input v-model="arkDinoForm.name"></el-input>
+        </el-form-item>
+        <el-form-item label="标签">
+          <el-input v-model="arkDinoForm.label"></el-input>
+        </el-form-item>
+        <el-form-item label="分类">
+          <el-select v-model="arkDinoForm.category">
+            <el-option :key="'Ammunition'" :value="'Ammunition'" :label="'弹药(Ammunition)'"></el-option>
+            <el-option :key="'Armor'" :value="'Armor'" :label="'护甲(Armor)'"></el-option>
+            <el-option :key="'Artifacts'" :value="'Artifacts'" :label="'遗物(Artifacts)'"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="图片">
+          <el-input v-model="arkDinoForm.imgUrl"></el-input>
+        </el-form-item>
+        <el-form-item label="实体ID">
+          <el-input v-model="arkDinoForm.entityId"></el-input>
+        </el-form-item>
+        <el-form-item label="蓝图">
+          <el-input v-model="arkDinoForm.blueprint"></el-input>
+        </el-form-item>
+        <el-form-item label="蓝图">
+          <el-input v-model="arkDinoForm.nameTag"></el-input>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import {ArkDinoInfo, PostDinoList, ReqArkItem} from "../../api/ARKItemsApi";
+import {ref} from "vue";
+
+let items = ref<ArkDinoInfo[]>([])
+let total = ref<number>(0)
+let pageNum = ref<number>(1)
+let pageSize = ref<number>(10)
+
+let arkDinoForm = ref<ArkDinoInfo>(new ArkDinoInfo(0, "", "", "", "", "", "", ""))
+let open = ref<boolean>(false)
+
+let searchForm = ref<ReqArkItem>(new ReqArkItem(pageNum.value, pageSize.value, "", "", "", ""))
+
+function replay() {
+  items.value = []
+  PostDinoList(searchForm.value, pageNum.value, pageSize.value).then((res) => {
+    if (res.code == 200) {
+      items.value = res.data.list
+      total.value = res.data.total
+    }
+  })
+}
+
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+}
+const handleCurrentChange = (val: number) => {
+  pageNum.value = val
+  replay()
+}
+
+function GetImg(url: string): string {
+  return url
+}
+
+
+replay()
+</script>
+<style scoped lang="scss">
+
+</style>

+ 184 - 0
ui/src/page/back/ARKItemListView.vue

@@ -0,0 +1,184 @@
+<template>
+  <div style="margin: 10px;padding: 10px">
+    <!--    <ArkItemSelect v-model="demo"/>-->
+    <el-form :model="searchForm" label-width="80px">
+      <el-row>
+        <el-col :span="4">
+          <el-form-item label="英文名">
+            <el-input v-model="searchForm.name"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="中文名">
+            <el-input v-model="searchForm.label"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="全部">
+            <el-input v-model="searchForm.allLike"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="4">
+          <el-form-item label="分类">
+            <el-select v-model="searchForm.category">
+              <el-option :key="'Ammunition'" :value="'Ammunition'" :label="'弹药(Ammunition)'"></el-option>
+              <el-option :key="'Armor'" :value="'Armor'" :label="'护甲(Armor)'"></el-option>
+              <el-option :key="'Artifacts'" :value="'Artifacts'" :label="'遗物(Artifacts)'"></el-option>
+              <el-option :key="'Attachments'" :value="'Attachments'" :label="'附件(Attachments)'"></el-option>
+              <el-option :key="'Chibi-pets'" :value="'Chibi-pets'" :label="'小宠物(Chibi-pets)'"></el-option>
+              <el-option :key="'Consumables'" :value="'Consumables'" :label="'消耗品(Consumables)'"></el-option>
+              <el-option :key="'Dye'" :value="'Dye'" :label="'染料(Dye)'"></el-option>
+              <el-option :key="'Eggs'" :value="'Eggs'" :label="'蛋(Eggs)'"></el-option>
+              <el-option :key="'Farming'" :value="'Farming'" :label="'农业(Farming)'"></el-option>
+              <el-option :key="'Recipes'" :value="'Recipes'" :label="'配方(Recipes)'"></el-option>
+              <el-option :key="'Resources'" :value="'Resources'" :label="'资源(Resources)'"></el-option>
+              <el-option :key="'Saddles'" :value="'Saddles'" :label="'鞍(Saddles)'"></el-option>
+              <el-option :key="'Seeds'" :value="'Seeds'" :label="'种子(Seeds)'"></el-option>
+              <el-option :key="'Skins'" :value="'Skins'" :label="'皮肤(Skins)'"></el-option>
+              <el-option :key="'Structures'" :value="'Structures'" :label="'结构(Structures)'"></el-option>
+              <el-option :key="'Tools'" :value="'Tools'" :label="'工具(Tools)'"></el-option>
+              <el-option :key="'Trophies'" :value="'Trophies'" :label="'奖杯(Trophies)'"></el-option>
+              <el-option :key="'Weapons'" :value="'Weapons'" :label="'武器(Weapons)'"></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="1"></el-col>
+        <el-col :span="1.5">
+          <el-button class="el-btn-margin" type="primary" @click="replay()">查询</el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button class="el-btn-margin" type="primary" @click="replay()">重置</el-button>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <el-table :data="items" style="width: 100%">
+      <el-table-column prop="id" label="ID" width="90"></el-table-column>
+      <el-table-column prop="imgUrl" label="图片">
+        <template #default="scope">
+          <el-image :src="scope.row.imgUrl" loading="lazy" style="max-width: 100px;max-height: 100px"></el-image>
+        </template>
+      </el-table-column>
+      <el-table-column prop="name" label="英文名"></el-table-column>
+      <el-table-column prop="label" label="中文名"></el-table-column>
+
+      <el-table-column prop="category" label="分类"></el-table-column>
+      <el-table-column prop="stackSize" label="堆栈大小" width="100"></el-table-column>
+      <el-table-column prop="itemId" label="物品ID" width="100">
+        <template #default="scope">
+          <el-text v-if="scope.row.itemId == 0 " type="info">无</el-text>
+          <el-text v-else type="primary">{{ scope.row.itemId == 0 ? '无' : scope.row.itemId }}</el-text>
+        </template>
+      </el-table-column>
+      <!--      <el-table-column prop="className" label="类名" ></el-table-column>-->
+      <!--      <el-table-column prop="blueprint" label="蓝图"></el-table-column>-->
+      <el-table-column label="功能">
+        <template #default="scope">
+          <el-button link type="primary" @click="OpenItemInfo(scope.row)">详情</el-button>
+          <el-button link type="success" @click="CopyBlueprintCode(scope.row.blueprint)">复制代码</el-button>
+        </template>
+      </el-table-column>
+
+    </el-table>
+
+    <el-pagination
+        v-model:current-page="pageNum"
+        v-model:page-size="pageSize"
+        :page-sizes="[10, 20, 50, 100,200]"
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+    />
+
+    <el-dialog v-model="openInfo" width="600">
+      <el-card>
+        <div slot="header">
+          <span>物品详情</span>
+        </div>
+        <el-form label-width="80px">
+          <el-form-item label="图片">
+            <el-image :src="ItemInfo.imgUrl" loading="lazy"
+                      style="max-width: 100px;max-height: 100px"></el-image>
+          </el-form-item>
+          <el-form-item label="英文名">
+            <el-text type="info">{{ ItemInfo.name }}</el-text>
+          </el-form-item>
+          <el-form-item label="中文名">
+            <el-text type="info">{{ ItemInfo.label }}</el-text>
+          </el-form-item>
+          <el-form-item label="分类">
+            <el-text type="info">{{ ItemInfo.category }}</el-text>
+          </el-form-item>
+          <el-form-item label="堆栈大小">
+            <el-text type="info">{{ ItemInfo.stackSize }}</el-text>
+          </el-form-item>
+          <el-form-item label="物品ID" v-if="ItemInfo.itemId!=0">
+            <el-text type="info"> {{ ItemInfo.itemId }}</el-text>
+          </el-form-item>
+          <el-form-item label="类名">
+            <el-text type="info"> {{ ItemInfo.className }}</el-text>
+          </el-form-item>
+          <el-form-item label="蓝图">
+            <el-text style=" margin-top: 9px;height: 80px;width: 400px;line-height: 20px" type="info">
+              {{ ItemInfo.blueprint }}
+            </el-text>
+          </el-form-item>
+        </el-form>
+<!--        <div slot="footer">-->
+<!--          <el-button type="primary" @click="openInfo=false">关闭</el-button>-->
+<!--        </div>-->
+      </el-card>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import {ArkItemInfo, PostItemList, ReqArkItem} from "../../api/ARKItemsApi";
+import {ref} from "vue";
+import {ElMessage} from "element-plus";
+
+let items = ref<ArkItemInfo[]>([])
+let total = ref<number>(0)
+let pageNum = ref<number>(1)
+let pageSize = ref<number>(10)
+
+
+let searchForm = ref<ReqArkItem>(new ReqArkItem(pageNum.value, pageSize.value, "", "", "", ""))
+
+function replay() {
+  items.value = []
+  PostItemList(searchForm.value, pageNum.value, pageSize.value).then((res) => {
+    if (res.code == 200) {
+      items.value = res.data.list
+      total.value = res.data.total
+    }
+  })
+}
+
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+  replay()
+}
+const handleCurrentChange = (val: number) => {
+  pageNum.value = val
+  replay()
+}
+//---------------------------------
+let openInfo = ref<boolean>(false)
+let ItemInfo = ref<ArkItemInfo>(new ArkItemInfo(0, "", "", "", "", 0, "", "", ""))
+
+function OpenItemInfo(row: ArkItemInfo) {
+  openInfo.value = true
+  ItemInfo.value = row
+}
+
+function CopyBlueprintCode(blueprint: string) {
+  navigator.clipboard.writeText("Cheat GiveItem \"" + blueprint.trim() + "\" 999 0 0")
+  ElMessage.success("复制成功")
+}
+
+replay()
+</script>
+<style scoped lang="scss">
+
+</style>

+ 18 - 0
ui/src/page/back/Home.vue

@@ -0,0 +1,18 @@
+<template>
+  <div style="display: flex;justify-content: center;margin-top: 30px">
+    <ImageNameButton :imageSrc="'/api/static/image/Element.png'" :text="'物品信息'" @click="Jump('/arkItem')"></ImageNameButton>
+    <ImageNameButton :imageSrc="'/api/static/image/Rex.png'" :text="'恐龙信息'" @click="Jump('/arkDino')"></ImageNameButton>
+  </div>
+
+</template>
+<script setup lang="ts">
+import ImageNameButton from "../../components/ImageNameButton/ImageNameButton.vue";
+
+function Jump(url:string) {
+  window.location.href = url;
+}
+
+</script>
+<style scoped lang="scss">
+
+</style>

+ 112 - 0
ui/src/page/back/index.vue

@@ -0,0 +1,112 @@
+<template>
+  <el-menu
+      :default-active="activeIndex"
+      class="el-menu-demo"
+      mode="horizontal"
+      background-color="#1e1f22"
+      text-color="#bcbec4"
+      active-text-color="#2ab19f"
+      @select="handleSelect">
+    <template v-for="menu in menus">
+      <template v-if="menu.subMenu.length > 0">
+        <el-sub-menu :index="menu.index">
+          <template #title>{{ menu.name }}</template>
+          <template v-for="subMenu in menu.subMenu">
+            <el-menu-item :index="subMenu.index" v-if="menu.path && menu.name" :key="subMenu.index"
+                          @click="JumpRouter(subMenu.path)">{{ subMenu.name }}
+            </el-menu-item>
+          </template>
+        </el-sub-menu>
+      </template>
+      <template v-else>
+        <el-menu-item v-if="menu.path && menu.name" :index="menu.index" @click="JumpRouter(menu.path)">{{ menu.name }}
+        </el-menu-item>
+      </template>
+    </template>
+<!--    <el-menu-item @click="ReplayConfig()">刷新-->
+<!--    </el-menu-item>-->
+  </el-menu>
+  <div class="enum-interval"></div>
+  <router-view/>
+</template>
+
+<script lang="ts" setup>
+import {ref} from 'vue'
+import router from "../../router";
+
+let activeIndex = ref<string>("1")
+
+class Menu {
+  index: string
+  name: string
+  path: string
+  subMenu: Menu[]
+
+  constructor(index: string, name: string, path: string, subMenu: Menu[]) {
+    this.index = index;
+    this.name = name;
+    this.path = path;
+    this.subMenu = subMenu;
+  }
+}
+
+const menus = ref<Menu[]>([
+  new Menu("1", "基本功能", "/", []),
+  new Menu("2", "物品资料", "/arkItem", []),
+  new Menu("3", "恐龙资料", "/arkDino", [])
+]);
+
+function GetNowMenu() {
+
+
+  let map = new Map<string, string>;
+  for (let i = 0; i < menus.value.length; i++) {
+    map.set(menus.value[i].path, menus.value[i])
+    if (menus.value[i].subMenu.length > 0) {
+      for (let j = 0; j < menus.value[i].subMenu.length; j++) {
+        map.set(menus.value[i].subMenu[j].path, menus.value[i].subMenu[j])
+      }
+    }
+  }
+
+  let path = router.currentRoute.value.path
+  let d = map.get(path)
+  activeIndex.value = d ? d.index : "1"
+
+
+  //获取路由
+  // let path = router.currentRoute.value.path
+  // for (let i = 0; i < menus.value.length; i++) {
+  //   if (menus.value[i].path == path) {
+  //     activeIndex.value = menus.value[i].index
+  //     return
+  //   }
+  // }
+  //
+}
+
+GetNowMenu()
+const handleSelect = (key: string, keyPath: string[]) => {
+}
+
+function JumpRouter(path: string) {
+  router.push({path: path})
+}
+
+
+</script>
+<style>
+.el-menu-demo {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 10;
+}
+
+.enum-interval {
+  height: 60px;
+  width: 100VW;
+
+}
+</style>

+ 35 - 0
ui/src/router/index.ts

@@ -0,0 +1,35 @@
+import {createRouter, createWebHistory, RouteRecordRaw} from "vue-router";
+
+// 2. 配置路由
+const routes: Array<RouteRecordRaw> = [
+
+    {
+        name: "back",
+        path: "/",
+        component: () => import("../page/back/index.vue"),
+        children: [
+            {
+                name: "backHome",
+                path: "/",
+                component: () => import("../page/back/Home.vue"),
+            },
+            {
+                name: "ARKItemsApi",
+                path: "arkItem",
+                component: () => import("../page/back/ARKItemListView.vue"),
+            },
+            {
+                name: "ARKDinosApi",
+                path: "arkDino",
+                component: () => import("../page/back/ARKDinoListView.vue"),
+            },
+        ],
+    },
+];
+
+// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
+const router = createRouter({
+    history: createWebHistory(import.meta.env.BASE_URL),
+    routes,
+});
+export default router

+ 67 - 0
ui/src/state/ItemList.ts

@@ -0,0 +1,67 @@
+// @ts-ignore
+import {createStore} from 'vuex'
+import {ArkDinoInfo, ArkInfo, ArkItemInfo, GetDinosApi, GetItemsApi} from "../api/ARKItemsApi";
+// 类似 Redux 中的建立状态树
+
+export default createStore({
+
+    // 1、 存储所有全局数据
+    state: {
+        itemList: <ArkItemInfo[]>[],
+        ItemMap: {
+            blueprintMap: new Map<string, ArkInfo>(),
+            labelMap: new Map<string, ArkInfo>(),
+            nameMap: new Map<string, ArkInfo>(),
+            categoryMap: new Map<string, ArkInfo>(),
+        },
+        dinoList: <ArkDinoInfo[]>[],
+
+    },
+
+    // 2、 需要通过计算获取state里的内容获取的数据
+    // 只能读取不可修改
+    getters: {},
+
+    //  3、定义对state的各种操作
+    // why不直接实现在mutation里需要写到action里?
+    // mtations不能执行异步操作。aq:从云端获取信息-->(等待云端反馈)更新到state异步操作
+    // 因此说:对于异步操作需要放到action里,简单的直接赋值操作可以直接放到mutation里
+    mutations: {
+        updateItemList(state: any) {
+            GetItemsApi().then(res => {
+                state.itemList = res.data.list;
+                state.itemList.forEach((item: ArkItemInfo) => {
+                    state.ItemMap.blueprintMap.set(item.blueprint, item)
+                    state.ItemMap.labelMap.set(item.label, item)
+                    state.ItemMap.nameMap.set(item.name, item)
+                    state.ItemMap.categoryMap.set(item.category, item)
+                })
+            })
+            GetDinosApi().then(res => {
+                state.dinoList = res.data.list;
+                state.dinoList.forEach((item: ArkDinoInfo) => {
+                    state.ItemMap.blueprintMap.set(item.blueprint, item)
+                    state.ItemMap.labelMap.set(item.label, item)
+                    state.ItemMap.nameMap.set(item.name, item)
+                    state.ItemMap.categoryMap.set(item.category, item)
+                })
+            })
+        }
+    },
+
+    // 3、定义对state的各种操作
+    // actions无法直接修改state,需要在mutations里更新
+    // mutation不支持异步,所以需要在action里写api到url
+    actions: {
+        // 比说action定义了更新state的操作
+        // 但是不可对其直接修改
+        // 所有的修改操作必须放到mutations里
+
+    },
+
+    // state中信息过长时
+    // 用来将state进行分割
+    // 如下,将state树分割出一个user state。
+    modules: {}
+})
+

+ 19 - 0
ui/src/style.css

@@ -0,0 +1,19 @@
+:root {
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+}
+html{
+
+  margin: 0;
+  padding: 0;
+}
+body {
+  margin: 0;
+  padding: 0;
+}
+div{
+  margin: 0;
+  padding: 0;
+}
+.el-btn-margin{
+  margin: 0  10px 10px 0;
+}

+ 1 - 0
ui/src/vite-env.d.ts

@@ -0,0 +1 @@
+/// <reference types="vite/client" />

+ 25 - 0
ui/tsconfig.json

@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
+    "skipLibCheck": true,
+
+    /* Bundler mode */
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": false,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "preserve",
+
+    /* Linting */
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true
+  },
+  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
+  "references": [{ "path": "./tsconfig.node.json" }]
+}

+ 11 - 0
ui/tsconfig.node.json

@@ -0,0 +1,11 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "skipLibCheck": true,
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "allowSyntheticDefaultImports": true,
+    "strict": true
+  },
+  "include": ["vite.config.ts"]
+}

+ 19 - 0
ui/vite.config.ts

@@ -0,0 +1,19 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [vue()],
+  server: {
+    port: 9666,
+    host: '0.0.0.0',
+    open: true,
+    proxy: {
+      '/api': {
+        target: 'http://localhost:9777',
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/api/, '')
+      }
+    }
+  }
+})