commit 4c6ab13210df9fa2c5a7ffcc19d9abc4b25b28ba Author: sunzhuangzhuang <961120009@qq.com> Date: Tue Aug 26 10:01:43 2025 +0800 提交代码 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8225baa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules +/dist diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/UniappTool.xml b/.idea/UniappTool.xml new file mode 100644 index 0000000..f7328e8 --- /dev/null +++ b/.idea/UniappTool.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/.idea/admin.iml b/.idea/admin.iml new file mode 100644 index 0000000..24643cc --- /dev/null +++ b/.idea/admin.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..42cb54d --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..960f5d1 --- /dev/null +++ b/index.html @@ -0,0 +1,80 @@ + + + + + + + 后台管理系统 + + + +
+
+
+
+
+ + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fea93f4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2265 @@ +{ + "name": "admin-system", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "admin-system", + "version": "0.0.0", + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "axios": "^1.6.2", + "dayjs": "^1.11.13", + "echarts": "^5.4.3", + "element-plus": "^2.4.4", + "nprogress": "^0.2.0", + "pinia": "^2.1.7", + "vue": "^3.3.11", + "vue-echarts": "^6.6.1", + "vue-router": "^4.2.5" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.5.2", + "sass": "^1.69.5", + "vite": "^5.0.8" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", + "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@element-plus/icons-vue": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", + "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@popperjs/core": { + "name": "@sxzz/popperjs-es", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", + "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", + "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", + "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", + "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", + "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", + "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", + "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", + "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", + "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", + "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", + "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", + "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", + "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", + "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", + "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", + "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", + "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", + "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", + "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", + "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", + "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "license": "MIT" + }, + "node_modules/@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", + "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz", + "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0 || ^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.17.tgz", + "integrity": "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/shared": "3.5.17", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz", + "integrity": "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz", + "integrity": "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@vue/compiler-core": "3.5.17", + "@vue/compiler-dom": "3.5.17", + "@vue/compiler-ssr": "3.5.17", + "@vue/shared": "3.5.17", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz", + "integrity": "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.17.tgz", + "integrity": "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.17.tgz", + "integrity": "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.17", + "@vue/shared": "3.5.17" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz", + "integrity": "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.17", + "@vue/runtime-core": "3.5.17", + "@vue/shared": "3.5.17", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.17.tgz", + "integrity": "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.17", + "@vue/shared": "3.5.17" + }, + "peerDependencies": { + "vue": "3.5.17" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.17.tgz", + "integrity": "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==", + "license": "MIT" + }, + "node_modules/@vueuse/core": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.13.0.tgz", + "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", + "license": "MIT", + "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/metadata": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.13.0.tgz", + "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.13.0.tgz", + "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", + "license": "MIT", + "dependencies": { + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/echarts": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz", + "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "5.6.1" + } + }, + "node_modules/element-plus": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.10.3.tgz", + "integrity": "sha512-OLpf0iekuvWJrz1+H9ybvem6TYTKSNk6L1QDA3tYq2YWbogKXJnWpHG1UAGKR1B7gx+vUH7M15VIH3EijE9Kgw==", + "license": "MIT", + "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.13", + "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==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.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, + "license": "MIT", + "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.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", + "license": "MIT", + "peerDependencies": { + "@types/lodash-es": "*", + "lodash": "*", + "lodash-es": "*" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", + "license": "BSD-3-Clause" + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resize-detector": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.3.0.tgz", + "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", + "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.44.2", + "@rollup/rollup-android-arm64": "4.44.2", + "@rollup/rollup-darwin-arm64": "4.44.2", + "@rollup/rollup-darwin-x64": "4.44.2", + "@rollup/rollup-freebsd-arm64": "4.44.2", + "@rollup/rollup-freebsd-x64": "4.44.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", + "@rollup/rollup-linux-arm-musleabihf": "4.44.2", + "@rollup/rollup-linux-arm64-gnu": "4.44.2", + "@rollup/rollup-linux-arm64-musl": "4.44.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-musl": "4.44.2", + "@rollup/rollup-linux-s390x-gnu": "4.44.2", + "@rollup/rollup-linux-x64-gnu": "4.44.2", + "@rollup/rollup-linux-x64-musl": "4.44.2", + "@rollup/rollup-win32-arm64-msvc": "4.44.2", + "@rollup/rollup-win32-ia32-msvc": "4.44.2", + "@rollup/rollup-win32-x64-msvc": "4.44.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/sass": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.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": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.17.tgz", + "integrity": "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.17", + "@vue/compiler-sfc": "3.5.17", + "@vue/runtime-dom": "3.5.17", + "@vue/server-renderer": "3.5.17", + "@vue/shared": "3.5.17" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "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/vue-echarts": { + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.7.3.tgz", + "integrity": "sha512-vXLKpALFjbPphW9IfQPOVfb1KjGZ/f8qa/FZHi9lZIWzAnQC1DgnmEK3pJgEkyo6EP7UnX6Bv/V3Ke7p+qCNXA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "resize-detector": "^0.3.0", + "vue-demi": "^0.13.11" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.5", + "@vue/runtime-core": "^3.0.0", + "echarts": "^5.4.1", + "vue": "^2.6.12 || ^3.1.1" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "@vue/runtime-core": { + "optional": true + } + } + }, + "node_modules/vue-echarts/node_modules/vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==", + "hasInstallScript": true, + "license": "MIT", + "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/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/zrender": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz", + "integrity": "sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f732a75 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "admin-system", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite --port 5174", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.1", + "axios": "^1.6.2", + "dayjs": "^1.11.13", + "echarts": "^5.4.3", + "element-plus": "^2.4.4", + "nprogress": "^0.2.0", + "pinia": "^2.1.7", + "vue": "^3.3.11", + "vue-echarts": "^6.6.1", + "vue-router": "^4.2.5" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^4.5.2", + "sass": "^1.69.5", + "vite": "^5.0.8" + } +} diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..16e07f3 --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/routes/agents.js b/routes/agents.js new file mode 100644 index 0000000..533715b --- /dev/null +++ b/routes/agents.js @@ -0,0 +1,591 @@ +const express = require('express'); +const router = express.Router(); +const { getDB } = require('../../database'); +const bcrypt = require('bcryptjs'); +const { auth, adminAuth } = require('../../middleware/auth'); + +// 创建管理员认证中间件组合 +const authenticateAdmin = [auth, adminAuth]; + +// 获取数据库连接 +const db = { + query: async (sql, params = []) => { + const connection = getDB(); + const [rows] = await connection.execute(sql, params); + return rows; + } +}; + +// 获取代理列表和统计信息 +router.get('/', authenticateAdmin, async (req, res) => { + try { + const { page = 1, limit = 20, status, city, search } = req.query; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(limit) || 20; + const offset = (pageNum - 1) * limitNum; + + // 构建查询条件 + let whereConditions = []; + let queryParams = []; + + if (status) { + whereConditions.push('ra.status = ?'); + queryParams.push(status); + } + + if (city) { + whereConditions.push('zr.city_name = ?'); + queryParams.push(city); + } + + if (search) { + whereConditions.push('(ra.real_name LIKE ? OR ra.phone LIKE ?)'); + queryParams.push(`%${search}%`, `%${search}%`); + } + + const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; + + // 查询代理列表 + const agentsQuery = ` + SELECT + ra.*, + u.real_name, + u.phone, + u.id_card, + zr.city_name, + zr.district_name, + ( + SELECT COUNT(DISTINCT merchant_id) + FROM agent_merchants + WHERE agent_id = ra.id + ) as merchant_count, + ( + SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) + FROM agent_commission_records + WHERE agent_id = ra.id + ) as total_commission, + 0 as paid_commission, + ( + SELECT CAST(COALESCE(SUM(commission_amount), 0) AS DECIMAL(10,2)) + FROM agent_commission_records + WHERE agent_id = ra.id + ) as pending_commission + FROM regional_agents ra + LEFT JOIN users u ON ra.user_id = u.id + LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id + ${whereClause} + ORDER BY ra.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} + `; + + const agents = await db.query(agentsQuery, queryParams); + + // 查询总数 + const countQuery = ` + SELECT COUNT(DISTINCT ra.id) as total + FROM regional_agents ra + LEFT JOIN users u ON ra.user_id = u.id + LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id + ${whereClause} + `; + + const totalResult = await db.query(countQuery, queryParams); + const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0; + + // 查询统计信息 + const statsQuery = ` + SELECT + COUNT(*) as total_agents, + COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_agents, + COUNT(CASE WHEN status = 'active' THEN 1 END) as active_agents, + CAST(COALESCE(SUM(commission_stats.total_commission), 0) AS DECIMAL(10,2)) as total_commission + FROM regional_agents ra + LEFT JOIN ( + SELECT + agent_id, + SUM(commission_amount) as total_commission + FROM agent_commission_records + GROUP BY agent_id + ) commission_stats ON ra.id = commission_stats.agent_id + `; + + const statsResult = await db.query(statsQuery); + const stats = statsResult && statsResult.length > 0 ? statsResult[0] : { + total_agents: 0, + pending_agents: 0, + active_agents: 0, + total_commission: 0 + }; + + res.json({ + success: true, + data: { + agents, + total: parseInt(total), + stats + } + }); + } catch (error) { + console.error('获取代理列表失败:', error); + res.status(500).json({ success: false, message: '获取代理列表失败' }); + } +}); + +// 获取代理详情 +router.get('/:id', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + + const agentQuery = ` + SELECT + ra.*, + u.real_name as name, + u.phone, + u.id_card, + CONCAT(u.city, ' ', zr.district_name) as address, + zr.city_name, + zr.district_name, + ( + SELECT COUNT(DISTINCT merchant_id) + FROM agent_merchants + WHERE agent_id = ra.id + ) as merchant_count, + ( + SELECT COALESCE(SUM(commission_amount), 0) + FROM agent_commission_records + WHERE agent_id = ra.id + ) as total_commission, + 0 as paid_commission, + ( + SELECT COALESCE(SUM(commission_amount), 0) + FROM agent_commission_records + WHERE agent_id = ra.id + ) as pending_commission + FROM regional_agents ra + LEFT JOIN users u ON ra.user_id = u.id + LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id + WHERE ra.id = ? + `; + + const agentResult = await db.query(agentQuery, [id]); + + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在' }); + } + + const agent = agentResult[0]; + + res.json({ + success: true, + data: agent + }); + } catch (error) { + console.error('获取代理详情失败:', error); + res.status(500).json({ success: false, message: '获取代理详情失败' }); + } +}); + +// 审核通过代理申请 +router.put('/:id/approve', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { password } = req.body; + + if (!password || password.length < 6) { + return res.status(400).json({ success: false, message: '密码长度不能少于6位' }); + } + + // 检查代理是否存在且状态为待审核 + const agents = await db.query( + 'SELECT * FROM regional_agents WHERE id = ? AND status = "pending"', + [id] + ); + + if (!agents || agents.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在或状态不正确' }); + } + + const agent = agents[0]; + + // 检查该区域是否已有其他激活的代理 + const existingActiveAgents = await db.query( + 'SELECT id FROM regional_agents WHERE region_id = ? AND status = "active" AND id != ?', + [agent.region_id, id] + ); + + if (existingActiveAgents && existingActiveAgents.length > 0) { + return res.status(400).json({ success: false, message: '该区域已有激活的代理,每个区域只能有一个代理账号' }); + } + + // 加密密码并更新用户表 + const hashedPassword = await bcrypt.hash(password, 10); + + // 更新用户密码 + await db.query( + `UPDATE users SET password = ? WHERE id = ( + SELECT user_id FROM regional_agents WHERE id = ? + )`, + [hashedPassword, id] + ); + + // 更新代理状态 + await db.query( + `UPDATE regional_agents + SET status = 'active', approved_at = NOW(), approved_by_admin_id = ? + WHERE id = ?`, + [req.user.id, id] + ); + + res.json({ + success: true, + message: '代理申请已通过' + }); + } catch (error) { + console.error('审核代理申请失败:', error); + res.status(500).json({ success: false, message: '审核代理申请失败' }); + } +}); + +// 拒绝代理申请 +router.put('/:id/reject', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { reason } = req.body; + + if (!reason || reason.trim() === '') { + return res.status(400).json({ success: false, message: '请输入拒绝原因' }); + } + + // 检查代理是否存在且状态为待审核 + const agentResult = await db.query( + 'SELECT * FROM regional_agents WHERE id = ? AND status = "pending"', + [id] + ); + + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在或状态不正确' }); + } + + const agent = agentResult[0]; + + // 更新代理状态 + await db.query( + `UPDATE regional_agents + SET status = 'rejected', reject_reason = ?, rejected_at = NOW(), rejected_by_admin_id = ? + WHERE id = ?`, + [reason.trim(), req.user.id, id] + ); + + res.json({ + success: true, + message: '代理申请已拒绝' + }); + } catch (error) { + console.error('拒绝代理申请失败:', error); + res.status(500).json({ success: false, message: '拒绝代理申请失败' }); + } +}); + +// 禁用代理 +router.put('/:id/disable', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + + // 检查代理是否存在且状态为激活 + const agentResult = await db.query( + 'SELECT * FROM regional_agents WHERE id = ? AND status = "active"', + [id] + ); + + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在或状态不正确' }); + } + + const agent = agentResult[0]; + + // 更新代理状态 + await db.query( + 'UPDATE regional_agents SET status = "disabled", disabled_at = NOW() WHERE id = ?', + [id] + ); + + res.json({ + success: true, + message: '代理已禁用' + }); + } catch (error) { + console.error('禁用代理失败:', error); + res.status(500).json({ success: false, message: '禁用代理失败' }); + } +}); + +// 启用代理 +router.put('/:id/enable', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + + // 检查代理是否存在且状态为禁用 + const agentResult = await db.query( + 'SELECT * FROM regional_agents WHERE id = ? AND status = "disabled"', + [id] + ); + + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在或状态不正确' }); + } + + const agent = agentResult[0]; + + // 检查该区域是否已有其他激活的代理 + const existingActiveAgentResult = await db.query( + 'SELECT id FROM regional_agents WHERE region_id = ? AND status = "active" AND id != ?', + [agent.region_id, id] + ); + + if (existingActiveAgentResult && existingActiveAgentResult.length > 0) { + return res.status(400).json({ success: false, message: '该区域已有激活的代理,每个区域只能有一个代理账号' }); + } + + // 更新代理状态 + await db.query( + 'UPDATE regional_agents SET status = "active", disabled_at = NULL WHERE id = ?', + [id] + ); + + res.json({ + success: true, + message: '代理已启用' + }); + } catch (error) { + console.error('启用代理失败:', error); + res.status(500).json({ success: false, message: '启用代理失败' }); + } +}); + +// 获取代理商户列表 +router.get('/:id/merchants', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { page = 1, limit = 20 } = req.query; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(limit) || 20; + const offset = (pageNum - 1) * limitNum; + + // 检查代理是否存在 + const agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]); + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在' }); + } + const agent = agentResult[0]; + + // 查询代理的商户列表 + const merchantsQuery = ` + SELECT + u.id, + u.real_name, + u.phone, + u.created_at, + am.created_at as joined_at, + COUNT(mo.id) as match_count, + COUNT(CASE WHEN mo.status = 'completed' THEN 1 END) as completed_matches + FROM agent_merchants am + JOIN users u ON am.merchant_id = u.id + LEFT JOIN matching_orders mo ON u.id = mo.initiator_id + WHERE am.agent_id = ? + GROUP BY u.id, am.created_at + ORDER BY am.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} + `; + + const merchants = await db.query(merchantsQuery, [id]); + + // 查询总数 + const totalResult = await db.query( + 'SELECT COUNT(*) as total FROM agent_merchants WHERE agent_id = ?', + [id] + ); + const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0; + + res.json({ + success: true, + data: { + merchants, + total: parseInt(total) + } + }); + } catch (error) { + console.error('获取代理商户列表失败:', error); + res.status(500).json({ success: false, message: '获取代理商户列表失败' }); + } +}); + +// 获取代理佣金记录 +router.get('/:id/commissions', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { page = 1, limit = 20 } = req.query; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(limit) || 20; + const offset = (pageNum - 1) * limitNum; + + // 检查代理是否存在 + const agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]); + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在' }); + } + const agent = agentResult[0]; + + // 查询佣金记录 + const commissionsQuery = ` + SELECT + acr.*, + u.phone as merchant_phone + FROM agent_commission_records acr + JOIN users u ON acr.merchant_id = u.id + WHERE acr.agent_id = ? + ORDER BY acr.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} + `; + + const commissions = await db.query(commissionsQuery, [id]); + + // 查询总数 + const totalResult = await db.query( + 'SELECT COUNT(*) as total FROM agent_commission_records WHERE agent_id = ?', + [id] + ); + const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0; + + res.json({ + success: true, + data: { + commissions, + total: parseInt(total) + } + }); + } catch (error) { + console.error('获取代理佣金记录失败:', error); + res.status(500).json({ success: false, message: '获取代理佣金记录失败' }); + } +}); + +// 获取代理商户的转账记录 +router.get('/:id/merchant-transfers', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { page = 1, limit = 20, merchant_id } = req.query; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(limit) || 20; + const offset = (pageNum - 1) * limitNum; + + // 检查代理是否存在 + const agentResult = await db.query('SELECT * FROM regional_agents WHERE id = ?', [id]); + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在' }); + } + + // 构建查询条件 + let whereConditions = ['am.agent_id = ?']; + let queryParams = [id]; + + if (merchant_id) { + whereConditions.push('t.from_user_id = ?'); + queryParams.push(merchant_id); + } + + const whereClause = whereConditions.join(' AND '); + + // 查询商户转账记录 + const transfersQuery = ` + SELECT + t.id, + t.from_user_id, + t.to_user_id, + t.amount, + t.status, + t.transfer_type, + t.description, + t.created_at, + t.confirmed_at, + from_user.real_name as from_real_name, + CONCAT(SUBSTRING(from_user.phone, 1, 3), '****', SUBSTRING(from_user.phone, -4)) as from_phone_masked, + to_user.real_name as to_real_name, + CONCAT(SUBSTRING(to_user.phone, 1, 3), '****', SUBSTRING(to_user.phone, -4)) as to_phone_masked + FROM agent_merchants am + JOIN transfers t ON am.merchant_id = t.from_user_id + LEFT JOIN users from_user ON t.from_user_id = from_user.id + LEFT JOIN users to_user ON t.to_user_id = to_user.id + WHERE ${whereClause} + ORDER BY t.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} + `; + + const transfers = await db.query(transfersQuery, queryParams); + + // 查询总数 + const totalQuery = ` + SELECT COUNT(*) as total + FROM agent_merchants am + JOIN transfers t ON am.merchant_id = t.from_user_id + WHERE ${whereClause} + `; + const totalResult = await db.query(totalQuery, queryParams); + const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0; + + res.json({ + success: true, + data: { + transfers, + total: parseInt(total) + } + }); + } catch (error) { + console.error('1:', error); + res.status(500).json({ success: false, message: '1获取代理商户转账记录失败' }); + } +}); + +/** + * 修改代理密码 + */ +router.put('/:id/password', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { password } = req.body; + + if (!password || password.length < 6) { + return res.status(400).json({ success: false, message: '密码长度不能少于6位' }); + } + + // 检查代理是否存在且状态为激活 + const agentResult = await db.query( + 'SELECT * FROM regional_agents WHERE id = ? AND status = "active"', + [id] + ); + + if (!agentResult || agentResult.length === 0) { + return res.status(404).json({ success: false, message: '代理不存在或状态不正确' }); + } + + // 加密新密码 + const hashedPassword = await bcrypt.hash(password, 10); + + // 更新用户表中的密码(与审核通过时的逻辑一致) + await db.query( + `UPDATE users SET password = ? WHERE id = ( + SELECT user_id FROM regional_agents WHERE id = ? + )`, + [hashedPassword, id] + ); + + res.json({ + success: true, + message: '代理密码修改成功' + }); + } catch (error) { + console.error('修改代理密码失败:', error); + res.status(500).json({ success: false, message: '修改代理密码失败' }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/withdrawals.js b/routes/withdrawals.js new file mode 100644 index 0000000..e67a63f --- /dev/null +++ b/routes/withdrawals.js @@ -0,0 +1,274 @@ +const express = require('express'); +const router = express.Router(); +const { getDB } = require('../../database'); +const { auth, adminAuth } = require('../../middleware/auth'); + +// 创建管理员认证中间件组合 +const authenticateAdmin = [auth, adminAuth]; + +// 获取数据库连接 +const db = { + query: async (sql, params = []) => { + const connection = getDB(); + const [rows] = await connection.execute(sql, params); + return rows; + } +}; + +/** + * 获取提现申请列表 + */ +router.get('/', authenticateAdmin, async (req, res) => { + try { + const { page = 1, limit = 20, status, agent_id } = req.query; + const pageNum = parseInt(page) || 1; + const limitNum = parseInt(limit) || 20; + const offset = (pageNum - 1) * limitNum; + + // 构建查询条件 + let whereConditions = []; + let queryParams = []; + + if (status) { + whereConditions.push('aw.status = ?'); + queryParams.push(status); + } + + if (agent_id) { + whereConditions.push('aw.agent_id = ?'); + queryParams.push(agent_id); + } + + const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; + + // 查询提现申请列表 + const withdrawalsQuery = ` + SELECT + aw.*, + ra.agent_code, + u.real_name as agent_name, + u.phone as agent_phone, + zr.city_name, + zr.district_name, + admin.real_name as processed_by_name + FROM agent_withdrawals aw + JOIN regional_agents ra ON aw.agent_id = ra.id + JOIN users u ON ra.user_id = u.id + LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id + LEFT JOIN users admin ON aw.processed_by = admin.id + ${whereClause} + ORDER BY aw.created_at DESC + LIMIT ${limitNum} OFFSET ${offset} + `; + + const withdrawals = await db.query(withdrawalsQuery, queryParams); + + // 查询总数 + const countQuery = ` + SELECT COUNT(*) as total + FROM agent_withdrawals aw + ${whereClause} + `; + + const totalResult = await db.query(countQuery, queryParams); + const total = totalResult && totalResult.length > 0 ? totalResult[0].total : 0; + + // 查询统计信息 + const statsQuery = ` + SELECT + COUNT(*) as total_applications, + COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending_count, + COUNT(CASE WHEN status = 'approved' THEN 1 END) as approved_count, + COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed_count, + COUNT(CASE WHEN status = 'rejected' THEN 1 END) as rejected_count, + CAST(COALESCE(SUM(CASE WHEN status = 'pending' THEN amount END), 0) AS DECIMAL(10,2)) as pending_amount, + CAST(COALESCE(SUM(CASE WHEN status = 'completed' THEN amount END), 0) AS DECIMAL(10,2)) as completed_amount + FROM agent_withdrawals + `; + + const statsResult = await db.query(statsQuery); + const stats = statsResult && statsResult.length > 0 ? statsResult[0] : { + total_applications: 0, + pending_count: 0, + approved_count: 0, + completed_count: 0, + rejected_count: 0, + pending_amount: 0, + completed_amount: 0 + }; + + res.json({ + success: true, + data: { + withdrawals, + total: parseInt(total), + stats + } + }); + } catch (error) { + console.error('获取提现申请列表失败:', error); + res.status(500).json({ success: false, message: '获取提现申请列表失败' }); + } +}); + +/** + * 审核提现申请 + */ +router.put('/:id/review', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const { action, admin_note } = req.body; + const adminId = req.user.id; + + if (!['approve', 'reject'].includes(action)) { + return res.status(400).json({ success: false, message: '无效的审核操作' }); + } + + // 检查提现申请是否存在且状态为待审核 + const withdrawalResult = await db.query( + 'SELECT * FROM agent_withdrawals WHERE id = ? AND status = "pending"', + [id] + ); + + if (!withdrawalResult || withdrawalResult.length === 0) { + return res.status(404).json({ success: false, message: '提现申请不存在或已处理' }); + } + + const withdrawal = withdrawalResult[0]; + const newStatus = action === 'approve' ? 'approved' : 'rejected'; + + // 开始事务 + const pool = getDB(); + const connection = await pool.getConnection(); + await connection.beginTransaction(); + + try { + // 更新提现申请状态 + await connection.execute( + 'UPDATE agent_withdrawals SET status = ?, admin_note = ?, processed_by = ?, processed_at = NOW() WHERE id = ?', + [newStatus, admin_note || null, adminId, id] + ); + + // 如果是拒绝,需要恢复代理的待提现金额 + if (action === 'reject') { + await connection.execute( + 'UPDATE regional_agents SET pending_withdrawal = pending_withdrawal - ? WHERE id = ?', + [withdrawal.amount, withdrawal.agent_id] + ); + } + + await connection.commit(); + connection.release(); // 释放连接回连接池 + + res.json({ + success: true, + message: action === 'approve' ? '提现申请已通过审核' : '提现申请已拒绝' + }); + } catch (error) { + await connection.rollback(); + connection.release(); // 释放连接回连接池 + throw error; + } + } catch (error) { + console.error('审核提现申请失败:', error); + res.status(500).json({ success: false, message: '审核提现申请失败' }); + } +}); + +/** + * 标记提现完成 + */ +router.put('/:id/complete', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + const adminId = req.user.id; + + // 检查提现申请是否存在且状态为已审核 + const withdrawalResult = await db.query( + 'SELECT * FROM agent_withdrawals WHERE id = ? AND status = "approved"', + [id] + ); + + if (!withdrawalResult || withdrawalResult.length === 0) { + return res.status(404).json({ success: false, message: '提现申请不存在或状态不正确' }); + } + + const withdrawal = withdrawalResult[0]; + + // 开始事务 + const pool = getDB(); + const connection = await pool.getConnection(); + await connection.beginTransaction(); + + try { + // 更新提现申请状态为已完成 + await connection.execute( + 'UPDATE agent_withdrawals SET status = "completed", processed_by = ?, processed_at = NOW() WHERE id = ?', + [adminId, id] + ); + + // 更新代理的已提现金额和待提现金额 + await connection.execute( + 'UPDATE regional_agents SET withdrawn_amount = withdrawn_amount + ?, pending_withdrawal = pending_withdrawal - ? WHERE id = ?', + [withdrawal.amount, withdrawal.amount, withdrawal.agent_id] + ); + + await connection.commit(); + connection.release(); // 释放连接回连接池 + + res.json({ + success: true, + message: '提现已标记为完成' + }); + } catch (error) { + await connection.rollback(); + connection.release(); // 释放连接回连接池 + throw error; + } + } catch (error) { + console.error('标记提现完成失败:', error); + res.status(500).json({ success: false, message: '标记提现完成失败' }); + } +}); + +/** + * 获取提现申请详情 + */ +router.get('/:id', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params; + + const withdrawalQuery = ` + SELECT + aw.*, + ra.agent_code, + u.real_name as agent_name, + u.phone as agent_phone, + zr.city_name, + zr.district_name, + admin.real_name as processed_by_name + FROM agent_withdrawals aw + JOIN regional_agents ra ON aw.agent_id = ra.id + JOIN users u ON ra.user_id = u.id + LEFT JOIN zhejiang_regions zr ON ra.region_id = zr.id + LEFT JOIN users admin ON aw.processed_by = admin.id + WHERE aw.id = ? + `; + + const withdrawalResult = await db.query(withdrawalQuery, [id]); + + if (!withdrawalResult || withdrawalResult.length === 0) { + return res.status(404).json({ success: false, message: '提现申请不存在' }); + } + + res.json({ + success: true, + data: withdrawalResult[0] + }); + } catch (error) { + console.error('获取提现申请详情失败:', error); + res.status(500).json({ success: false, message: '获取提现申请详情失败' }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..ac55e9b --- /dev/null +++ b/src/App.vue @@ -0,0 +1,237 @@ + + + + + \ No newline at end of file diff --git a/src/components/Captcha.vue b/src/components/Captcha.vue new file mode 100644 index 0000000..99d6a86 --- /dev/null +++ b/src/components/Captcha.vue @@ -0,0 +1,298 @@ + + + + + \ No newline at end of file diff --git a/src/components/ImageUpload.vue b/src/components/ImageUpload.vue new file mode 100644 index 0000000..1b08de6 --- /dev/null +++ b/src/components/ImageUpload.vue @@ -0,0 +1,345 @@ + + + + + \ No newline at end of file diff --git a/src/layout/Layout.vue b/src/layout/Layout.vue new file mode 100644 index 0000000..3382ac8 --- /dev/null +++ b/src/layout/Layout.vue @@ -0,0 +1,597 @@ + + + + + \ No newline at end of file diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..a84b46d --- /dev/null +++ b/src/main.js @@ -0,0 +1,53 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import zhCn from 'element-plus/es/locale/lang/zh-cn' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import dayjs from 'dayjs' + +import App from './App.vue' +import router from './router' +import { useUserStore } from './stores/user' +import './style.css' + +// 配置 NProgress +NProgress.configure({ + showSpinner: false, + minimum: 0.2, + easing: 'ease', + speed: 500 +}) + +const app = createApp(App) +const pinia = createPinia() + +// 注册所有图标 +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +app.use(pinia) +app.use(router) +app.use(ElementPlus, { + locale: zhCn, +}) + +// 全局注册dayjs +app.config.globalProperties.$dayjs = dayjs + +// 应用初始化后检查用户状态 +app.mount('#app') + +// 初始化用户状态检查 +const userStore = useUserStore() +if (userStore.isAuthenticated) { + // 如果用户已登录,启动状态检查 + userStore.checkAuth().then((isValid) => { + if (isValid) { + userStore.startStatusCheck() + } + }) +} \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..401e808 --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,281 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useUserStore } from '@/stores/user' +import { ElMessage } from 'element-plus' +import NProgress from 'nprogress' + +const routes = [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/Login.vue'), + meta: { + title: '登录 - 炬融圈', + requiresAuth: false + } + }, + { + path: '/', + component: () => import('@/layout/Layout.vue'), + redirect: '/dashboard', + meta: { + requiresAuth: true + }, + children: [ + { + path: 'dashboard', + name: 'Dashboard', + component: () => import('@/views/Dashboard.vue'), + meta: { + title: '仪表盘 - 炬融圈', + icon: 'Odometer' + } + }, + { + path: 'users', + name: 'Users', + component: () => import('@/views/Users.vue'), + meta: { + title: '用户管理 - 炬融圈', + icon: 'User', + requiresAdmin: true + } + }, + { + path: 'user-audit', + name: 'UserAudit', + component: () => import('@/views/UserAudit.vue'), + meta: { + title: '用户审核 - 炬融圈', + icon: 'DocumentChecked', + requiresAdmin: true + } + }, + { + path: 'registration-codes', + name: 'RegistrationCodes', + component: () => import('@/views/RegistrationCodes.vue'), + meta: { + title: '激活码管理 - 炬融圈', + icon: 'Ticket', + requiresAdmin: true + } + }, + { + path: 'products', + name: 'Products', + component: () => import('@/views/Products.vue'), + meta: { + title: '商品管理 - 积分商城管理系统', + icon: 'Goods', + requiresAdmin: true + } + }, + { + path: 'products/create', + name: 'CreateProduct', + component: () => import('@/views/ProductForm.vue'), + meta: { + title: '创建商品 - 积分商城管理系统', + icon: 'Plus', + requiresAdmin: true + } + }, + { + path: 'products/edit/:id', + name: 'EditProduct', + component: () => import('@/views/ProductForm.vue'), + meta: { + title: '编辑商品 - 积分商城管理系统', + icon: 'EditPen', + requiresAdmin: true + } + }, + { + path: 'orders', + name: 'Orders', + component: () => import('@/views/Orders.vue'), + meta: { + title: '订单管理 - 积分商城管理系统', + icon: 'List', + requiresAdmin: true + } + }, + { + path: 'points', + name: 'Points', + component: () => import('@/views/Points.vue'), + meta: { + title: '积分管理 - 积分商城管理系统', + icon: 'Coin', + requiresAdmin: true + } + }, + + { + path: 'transfers', + name: 'Transfers', + component: () => import('@/views/Transfers.vue'), + meta: { + title: '转账管理 - 炬融圈', + icon: 'Money', + requiresAdmin: true + } + }, + { + path: 'daily-transfer-stats', + name: 'DailyTransferStats', + component: () => import('@/views/DailyTransferStats.vue'), + meta: { + title: '昨日转账统计 - 炬融圈', + icon: 'DataAnalysis', + requiresAdmin: true + } + }, + // { + // path: 'matching-management', + // name: 'MatchingManagement', + // component: () => import('@/views/MatchingManagement.vue'), + // meta: { + // title: '匹配管理 - 炬融圈', + // icon: 'Connection', + // requiresAdmin: true + // } + // }, + { + path: 'risk-management', + name: 'RiskManagement', + component: () => import('@/views/RiskManagement.vue'), + meta: { + title: '风险管理 - 炬融圈', + icon: 'Warning', + requiresAdmin: true + } + }, + { + path: 'agents', + name: 'Agents', + component: () => import('@/views/Agents.vue'), + meta: { + title: '代理管理 - 炬融圈', + icon: 'Avatar', + requiresAdmin: true + } + }, + { + path: 'withdrawals', + name: 'Withdrawals', + component: () => import('@/views/Withdrawals.vue'), + meta: { + title: '提现管理 - 炬融圈', + icon: 'CreditCard', + requiresAdmin: true + } + }, + // { + // path: 'database-monitor', + // name: 'DatabaseMonitor', + // component: () => import('@/views/DatabaseMonitor.vue'), + // meta: { + // title: '数据库监控 - 炬融圈', + // icon: 'Monitor', + // requiresAdmin: true + // } + // }, + { + path: 'profile', + name: 'Profile', + component: () => import('@/views/Profile.vue'), + meta: { + title: '个人资料 - 炬融圈', + icon: 'UserFilled' + } + } + // { + // path: 'settings', + // name: 'Settings', + // component: () => import('@/views/Settings.vue'), + // meta: { + // title: '系统设置 - 炬融圈', + // icon: 'Setting', + // requiresAdmin: true + // } + // } + ] + }, + { + path: '/404', + name: 'NotFound', + component: () => import('@/views/404.vue'), + meta: { + title: '页面未找到 - 炬融圈' + } + }, + { + path: '/:pathMatch(.*)*', + redirect: '/404' + } +] + +const router = createRouter({ + history: createWebHistory('/admin/'), + routes +}) + +// 路由守卫 +router.beforeEach(async (to, from, next) => { + NProgress.start() + + const userStore = useUserStore() + + // 设置页面标题 + if (to.meta.title) { + document.title = to.meta.title + } + + // 检查是否需要认证 + if (to.meta.requiresAuth !== false) { + if (!userStore.isAuthenticated) { + // 只在没有token或用户信息时才尝试从本地存储恢复登录状态 + const token = localStorage.getItem('admin_token') + const userStr = localStorage.getItem('admin_user') + + if (token && userStr) { + try { + const user = JSON.parse(userStr) + userStore.token = token + userStore.user = user + } catch (error) { + console.error('解析用户信息失败:', error) + localStorage.removeItem('admin_token') + localStorage.removeItem('admin_user') + } + } + + if (!userStore.isAuthenticated) { + next('/login') + return + } + } + + // 检查管理员权限 + if (to.meta.requiresAdmin && !userStore.isAdmin) { + ElMessage.error('您没有权限访问此页面') + next('/dashboard') + return + } + } + + // 如果已登录用户访问登录页,重定向到仪表盘 + if (to.name === 'Login' && userStore.isAuthenticated) { + next('/dashboard') + return + } + + next() +}) + +router.afterEach(() => { + NProgress.done() +}) + +export default router \ No newline at end of file diff --git a/src/stores/user.js b/src/stores/user.js new file mode 100644 index 0000000..dc5c040 --- /dev/null +++ b/src/stores/user.js @@ -0,0 +1,188 @@ +import { defineStore } from 'pinia' +import { ElMessage } from 'element-plus' +import api from '@/utils/api' + +export const useUserStore = defineStore('user', { + state: () => ({ + user: null, + token: localStorage.getItem('admin_token') || null, + loading: false, + statusCheckInterval: null + }), + + getters: { + isAuthenticated: (state) => !!state.token && !!state.user, + isAdmin: (state) => state.user?.role === 'admin' + }, + + actions: { + // 登录 + async login(credentials) { + this.loading = true + try { + const response = await api.auth.login(credentials) + const { token, user } = response.data + + this.token = token + this.user = user + + // 存储到本地存储 + localStorage.setItem('admin_token', token) + localStorage.setItem('admin_user', JSON.stringify(user)) + + this.startStatusCheck() // 登录成功后开始状态检查 + ElMessage.success(`欢迎回来,${user.username}!`) + return { success: true } + } catch (error) { + const message = error.response?.data?.message || '登录失败' + ElMessage.error(message) + return { success: false, message } + } finally { + this.loading = false + } + }, + + // 登出 + async logout() { + try { + this.stopStatusCheck() // 登出时停止状态检查 + + // 清除状态 + this.user = null + this.token = null + + // 清除本地存储 + localStorage.removeItem('admin_token') + localStorage.removeItem('admin_user') + + ElMessage.success('退出成功,期待您的再次光临!') + } catch (error) { + console.error('登出失败:', error) + } + }, + + // 检查认证状态(仅在需要时验证token有效性) + async checkAuth() { + const token = localStorage.getItem('admin_token') + const userStr = localStorage.getItem('admin_user') + + if (!token || !userStr) { + return false + } + + try { + // 先从本地存储恢复状态 + const user = JSON.parse(userStr) + this.token = token + this.user = user + + // 可选:验证token是否仍然有效(仅在必要时调用) + // 这里不主动验证,让具体的API调用时自然验证 + return true + } catch (error) { + console.error('认证检查失败:', error) + // 清除无效的本地存储数据 + localStorage.removeItem('admin_token') + localStorage.removeItem('admin_user') + return false + } + }, + + // 更新个人信息 + async updateProfile(profileData) { + this.loading = true + try { + const response = await api.users.updateUser(this.user.id, profileData) + + this.user = { ...this.user, ...response.data } + localStorage.setItem('admin_user', JSON.stringify(this.user)) + + ElMessage.success('个人信息更新成功') + return { success: true } + } catch (error) { + const message = error.response?.data?.message || '更新失败' + ElMessage.error(message) + return { success: false, message } + } finally { + this.loading = false + } + }, + + // 修改密码 + async changePassword(passwordData) { + this.loading = true + try { + await api.auth.changePassword(passwordData) + ElMessage.success('密码修改成功,请重新登录') + + // 修改密码后需要重新登录 + setTimeout(() => { + this.logout() + }, 1500) + + return { success: true } + } catch (error) { + const message = error.response?.data?.message || '密码修改失败' + ElMessage.error(message) + return { success: false, message } + } finally { + this.loading = false + } + }, + + // 获取用户详情 + async getUserDetails(userId) { + try { + const response = await api.users.getUserById(userId) + return response.data + } catch (error) { + const message = error.response?.data?.message || '获取用户详情失败' + ElMessage.error(message) + throw error + } + }, + + // 获取当前用户信息 + async fetchUserInfo() { + try { + const response = await api.auth.getCurrentUser() + this.user = response.data.user + localStorage.setItem('admin_user', JSON.stringify(this.user)) + return { success: true } + } catch (error) { + const message = error.response?.data?.message || '获取用户信息失败' + console.error('获取用户信息失败:', error) + return { success: false, message } + } + }, + + // 开始状态检查 + startStatusCheck() { + // 如果已经有定时器在运行,先清除 + if (this.statusCheckInterval) { + clearInterval(this.statusCheckInterval) + } + + // 每5分钟检查一次用户状态 + this.statusCheckInterval = setInterval(async () => { + if (this.isAuthenticated) { + try { + await api.auth.getCurrentUser() + } catch (error) { + // 如果请求失败,说明token可能已失效或用户被拉黑 + // api拦截器会自动处理这些情况 + console.log('用户状态检查失败,可能已被拉黑或token失效') + } + } + }, 5 * 60 * 1000) // 5分钟 + }, + + // 停止状态检查 + stopStatusCheck() { + if (this.statusCheckInterval) { + clearInterval(this.statusCheckInterval) + this.statusCheckInterval = null + } + } + } +}) \ No newline at end of file diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..72a2fcc --- /dev/null +++ b/src/style.css @@ -0,0 +1,298 @@ +/* 样式重置 */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body { + height: 100%; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* 滚动条样式 */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +/* 链接样式 */ +a { + color: #409eff; + text-decoration: none; + transition: color 0.3s ease; +} + +a:hover { + color: #66b1ff; +} + +/* 工具类 */ +.text-left { text-align: left !important; } +.text-center { text-align: center !important; } +.text-right { text-align: right !important; } +.text-justify { text-align: justify !important; } + +.float-left { float: left !important; } +.float-right { float: right !important; } +.clearfix::after { + content: ""; + display: table; + clear: both; +} + +.d-none { display: none !important; } +.d-inline { display: inline !important; } +.d-inline-block { display: inline-block !important; } +.d-block { display: block !important; } +.d-flex { display: flex !important; } +.d-inline-flex { display: inline-flex !important; } + +.flex-row { flex-direction: row !important; } +.flex-column { flex-direction: column !important; } +.flex-wrap { flex-wrap: wrap !important; } +.flex-nowrap { flex-wrap: nowrap !important; } + +.justify-content-start { justify-content: flex-start !important; } +.justify-content-end { justify-content: flex-end !important; } +.justify-content-center { justify-content: center !important; } +.justify-content-between { justify-content: space-between !important; } +.justify-content-around { justify-content: space-around !important; } + +.align-items-start { align-items: flex-start !important; } +.align-items-end { align-items: flex-end !important; } +.align-items-center { align-items: center !important; } +.align-items-baseline { align-items: baseline !important; } +.align-items-stretch { align-items: stretch !important; } + +.flex-fill { flex: 1 1 auto !important; } +.flex-grow-0 { flex-grow: 0 !important; } +.flex-grow-1 { flex-grow: 1 !important; } +.flex-shrink-0 { flex-shrink: 0 !important; } +.flex-shrink-1 { flex-shrink: 1 !important; } + +/* 间距工具类 */ +.m-0 { margin: 0 !important; } +.m-1 { margin: 0.25rem !important; } +.m-2 { margin: 0.5rem !important; } +.m-3 { margin: 1rem !important; } +.m-4 { margin: 1.5rem !important; } +.m-5 { margin: 3rem !important; } + +.mt-0 { margin-top: 0 !important; } +.mt-1 { margin-top: 0.25rem !important; } +.mt-2 { margin-top: 0.5rem !important; } +.mt-3 { margin-top: 1rem !important; } +.mt-4 { margin-top: 1.5rem !important; } +.mt-5 { margin-top: 3rem !important; } + +.mb-0 { margin-bottom: 0 !important; } +.mb-1 { margin-bottom: 0.25rem !important; } +.mb-2 { margin-bottom: 0.5rem !important; } +.mb-3 { margin-bottom: 1rem !important; } +.mb-4 { margin-bottom: 1.5rem !important; } +.mb-5 { margin-bottom: 3rem !important; } + +.ml-0 { margin-left: 0 !important; } +.ml-1 { margin-left: 0.25rem !important; } +.ml-2 { margin-left: 0.5rem !important; } +.ml-3 { margin-left: 1rem !important; } +.ml-4 { margin-left: 1.5rem !important; } +.ml-5 { margin-left: 3rem !important; } + +.mr-0 { margin-right: 0 !important; } +.mr-1 { margin-right: 0.25rem !important; } +.mr-2 { margin-right: 0.5rem !important; } +.mr-3 { margin-right: 1rem !important; } +.mr-4 { margin-right: 1.5rem !important; } +.mr-5 { margin-right: 3rem !important; } + +.p-0 { padding: 0 !important; } +.p-1 { padding: 0.25rem !important; } +.p-2 { padding: 0.5rem !important; } +.p-3 { padding: 1rem !important; } +.p-4 { padding: 1.5rem !important; } +.p-5 { padding: 3rem !important; } + +.pt-0 { padding-top: 0 !important; } +.pt-1 { padding-top: 0.25rem !important; } +.pt-2 { padding-top: 0.5rem !important; } +.pt-3 { padding-top: 1rem !important; } +.pt-4 { padding-top: 1.5rem !important; } +.pt-5 { padding-top: 3rem !important; } + +.pb-0 { padding-bottom: 0 !important; } +.pb-1 { padding-bottom: 0.25rem !important; } +.pb-2 { padding-bottom: 0.5rem !important; } +.pb-3 { padding-bottom: 1rem !important; } +.pb-4 { padding-bottom: 1.5rem !important; } +.pb-5 { padding-bottom: 3rem !important; } + +.pl-0 { padding-left: 0 !important; } +.pl-1 { padding-left: 0.25rem !important; } +.pl-2 { padding-left: 0.5rem !important; } +.pl-3 { padding-left: 1rem !important; } +.pl-4 { padding-left: 1.5rem !important; } +.pl-5 { padding-left: 3rem !important; } + +.pr-0 { padding-right: 0 !important; } +.pr-1 { padding-right: 0.25rem !important; } +.pr-2 { padding-right: 0.5rem !important; } +.pr-3 { padding-right: 1rem !important; } +.pr-4 { padding-right: 1.5rem !important; } +.pr-5 { padding-right: 3rem !important; } + +/* 宽度高度 */ +.w-25 { width: 25% !important; } +.w-50 { width: 50% !important; } +.w-75 { width: 75% !important; } +.w-100 { width: 100% !important; } +.w-auto { width: auto !important; } + +.h-25 { height: 25% !important; } +.h-50 { height: 50% !important; } +.h-75 { height: 75% !important; } +.h-100 { height: 100% !important; } +.h-auto { height: auto !important; } + +/* 颜色 */ +.text-primary { color: #409eff !important; } +.text-success { color: #67c23a !important; } +.text-warning { color: #e6a23c !important; } +.text-danger { color: #f56c6c !important; } +.text-info { color: #909399 !important; } +.text-light { color: #f8f9fa !important; } +.text-dark { color: #343a40 !important; } +.text-muted { color: #6c757d !important; } +.text-white { color: #fff !important; } + +.bg-primary { background-color: #409eff !important; } +.bg-success { background-color: #67c23a !important; } +.bg-warning { background-color: #e6a23c !important; } +.bg-danger { background-color: #f56c6c !important; } +.bg-info { background-color: #909399 !important; } +.bg-light { background-color: #f8f9fa !important; } +.bg-dark { background-color: #343a40 !important; } +.bg-white { background-color: #fff !important; } + +/* 边框 */ +.border { border: 1px solid #dee2e6 !important; } +.border-top { border-top: 1px solid #dee2e6 !important; } +.border-right { border-right: 1px solid #dee2e6 !important; } +.border-bottom { border-bottom: 1px solid #dee2e6 !important; } +.border-left { border-left: 1px solid #dee2e6 !important; } +.border-0 { border: 0 !important; } + +.rounded { border-radius: 0.25rem !important; } +.rounded-top { border-top-left-radius: 0.25rem !important; border-top-right-radius: 0.25rem !important; } +.rounded-right { border-top-right-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } +.rounded-bottom { border-bottom-right-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } +.rounded-left { border-top-left-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } +.rounded-circle { border-radius: 50% !important; } +.rounded-0 { border-radius: 0 !important; } + +/* 阴影 */ +.shadow-sm { box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } +.shadow { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } +.shadow-lg { box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } +.shadow-none { box-shadow: none !important; } + +/* 过渡动画 */ +.transition { transition: all 0.3s ease !important; } +.transition-fast { transition: all 0.15s ease !important; } +.transition-slow { transition: all 0.6s ease !important; } + +/* 鼠标样式 */ +.cursor-pointer { cursor: pointer !important; } +.cursor-default { cursor: default !important; } +.cursor-not-allowed { cursor: not-allowed !important; } + +/* 溢出处理 */ +.overflow-auto { overflow: auto !important; } +.overflow-hidden { overflow: hidden !important; } +.overflow-visible { overflow: visible !important; } +.overflow-scroll { overflow: scroll !important; } + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-break { + word-wrap: break-word !important; + word-break: break-word !important; +} + +/* 响应式隐藏 */ +@media (max-width: 576px) { + .d-sm-none { display: none !important; } +} + +@media (max-width: 768px) { + .d-md-none { display: none !important; } +} + +@media (max-width: 992px) { + .d-lg-none { display: none !important; } +} + +@media (max-width: 1200px) { + .d-xl-none { display: none !important; } +} + +/* 自定义动画 */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideInLeft { + from { + transform: translateX(-100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideInRight { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +.animate-fadeIn { + animation: fadeIn 0.5s ease-in-out; +} + +.animate-slideInLeft { + animation: slideInLeft 0.5s ease-in-out; +} + +.animate-slideInRight { + animation: slideInRight 0.5s ease-in-out; +} \ No newline at end of file diff --git a/src/utils/api.js b/src/utils/api.js new file mode 100644 index 0000000..f066785 --- /dev/null +++ b/src/utils/api.js @@ -0,0 +1,299 @@ +import axios from 'axios' +import { ElMessage, ElLoading } from 'element-plus' +import NProgress from 'nprogress' + +// 创建axios实例 +const request = axios.create({ + baseURL: '/api', + timeout: 10000, + headers: { + 'Content-Type': 'application/json' + } +}) + +let loadingInstance = null +let requestCount = 0 +let isLoggingOut = false // 防止重复登出 + +// 显示加载 +const showLoading = () => { + if (requestCount === 0) { + loadingInstance = ElLoading.service({ + text: '加载中...', + background: 'rgba(0, 0, 0, 0.7)' + }) + } + requestCount++ +} + +// 隐藏加载 +const hideLoading = () => { + requestCount-- + if (requestCount <= 0) { + requestCount = 0 + if (loadingInstance) { + loadingInstance.close() + loadingInstance = null + } + } +} + +// 请求拦截器 +request.interceptors.request.use( + (config) => { + // 开始进度条 + NProgress.start() + + // 显示加载动画(除了某些不需要的请求) + if (!config.hideLoading) { + showLoading() + } + + // 添加认证token + const token = localStorage.getItem('admin_token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + + return config + }, + (error) => { + hideLoading() + NProgress.done() + return Promise.reject(error) + } +) + +// 响应拦截器 +request.interceptors.response.use( + (response) => { + hideLoading() + NProgress.done() + return response + }, + (error) => { + hideLoading() + NProgress.done() + + const { response } = error + + if (response) { + switch (response.status) { + case 401: + // 防止重复处理401错误 + if (!isLoggingOut) { + isLoggingOut = true + + // 只在非登录页面显示错误消息 + if (window.location.pathname !== '/admin/login') { + ElMessage.error('登录已过期,请重新登录') + } + + // 清除本地存储 + localStorage.removeItem('admin_token') + localStorage.removeItem('admin_user') + + // 立即跳转到登录页,减少延迟 + setTimeout(() => { + if (typeof window !== 'undefined' && window.location.pathname !== '/admin/login') { + window.location.href = '/admin/login' + } + // 重置标志 + setTimeout(() => { + isLoggingOut = false + }, 1000) + }, 500) + } + break + case 403: + // 检查是否是用户被拉黑 + if (response.data.code === 'USER_BLACKLISTED') { + // 防止重复处理拉黑错误 + if (!isLoggingOut) { + isLoggingOut = true + + ElMessage.error(response.data.message || '账户已被拉黑,请联系管理员') + + // 清除本地存储 + localStorage.removeItem('admin_token') + localStorage.removeItem('admin_user') + + // 跳转到登录页 + setTimeout(() => { + if (typeof window !== 'undefined' && window.location.pathname !== '/admin/login') { + window.location.href = '/admin/login' + } + // 重置标志 + setTimeout(() => { + isLoggingOut = false + }, 1000) + }, 500) + } + } else { + ElMessage.error('没有权限访问此资源') + } + break + case 404: + ElMessage.error('请求的资源不存在') + break + case 422: + ElMessage.error(response.data.message || '请求参数错误') + break + case 429: + ElMessage.error('请求过于频繁,请稍后再试') + break + case 500: + ElMessage.error('服务器内部错误') + break + default: + ElMessage.error(response.data.error.message || '请求失败') + } + } else { + ElMessage.error('网络错误,请检查网络连接') + } + + return Promise.reject(error) + } +) + +// API接口定义 +const api = { + // 认证相关 + auth: { + login: (data) => request.post('/auth/login', data), + register: (data) => request.post('/auth/register', data), + getCurrentUser: () => request.get('/auth/me'), + changePassword: (data) => request.put('/auth/change-password', data) + }, + + // 用户管理 + users: { + getUsers: (params) => request.get('/users', { params }), + getUserById: (id) => request.get(`/users/${id}`), + createUser: (data) => request.post('/users', data), + updateUser: (id, data) => request.put(`/users/${id}`, data), + deleteUser: (id) => request.delete(`/users/${id}`), + getUserStats: () => request.get('/users/stats'), + getUserGrowthTrend: (params) => request.get('/users/growth-trend', { params }), + getDailyRevenue: (params) => request.get('/users/daily-revenue', { params }) + }, + + // 积分管理 + points: { + getStats: () => request.get('/points/stats'), + getHistory: (params) => request.get('/points/history', { params }), + adjustPoints: (data) => request.post('/points/adjust', data) + }, + + // 文件上传 + upload: { + uploadImage: (file) => { + const formData = new FormData() + formData.append('image', file) + return request.post('/upload/image', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + }, + uploadFile: (file) => { + const formData = new FormData() + formData.append('file', file) + return request.post('/upload/file', formData, { + headers: { + 'Content-Type': 'multipart/form-data' + } + }) + } + }, + + // 系统统计 + dashboard: { + getStats: () => request.get('/dashboard/stats', { hideLoading: true }), + getChartData: (type) => request.get(`/dashboard/charts/${type}`, { hideLoading: true }) + }, + + // 激活码管理 + registrationCodes: { + getRegistrationCodes: (params) => request.get('/users/registration-codes', { params }), + createRegistrationCode: (data) => request.post('/users/registration-codes', data), + batchCreateRegistrationCodes: (data) => request.post('/users/registration-codes/batch', data), + deleteRegistrationCode: (id) => request.delete(`/users/registration-codes/${id}`) + }, + + // 转账管理 + transfers: { + getTransfers: (params) => request.get('/transfers', { params }), + getTransferById: (id) => request.get(`/transfers/${id}`), + confirmTransfer: (id) => request.post('/transfers/confirm', { transfer_id: id }), + rejectTransfer: (id) => request.post('/transfers/reject', { transfer_id: id }), + confirmReceived: (id) => request.post('/transfers/confirm-received', { transfer_id: id }), + confirmNotReceived: (id) => request.post('/transfers/confirm-not-received', { transfer_id: id }), + getTransferStats: () => request.get('/transfers/stats'), + getTransferTrend: (params) => request.get('/transfers/trend', { params }), + getPublicAccount: () => request.get('/transfers/public-account'), + getUserTransfers: (userId, params) => request.get(`/transfers/user/${userId}`, { params }), + getPendingTransfers: () => request.get('/transfers/pending'), + getAccountInfo: (userId) => request.get(`/transfers/account/${userId}`), + createAdminTransfer: (fromUserId, data) => { + return request.post('/transfers/admin/create', { + from_user_id: fromUserId, + ...data + }) + }, + removeBadDebt: (transferId, reason) => request.post(`/transfers/remove-bad-debt/${transferId}`, { reason }), + // 强制变更转账状态 + forceChangeStatus: (transferId, data) => request.post(`/transfers/force-change-status/${transferId}`, data), + + // 数据库监控 + getDatabaseStatus: () => request.get('/transfers/admin/database/status'), + getDatabaseReport: () => request.get('/transfers/admin/database/report'), + + // 待处理匹配订单相关 + getPendingAllocations: (params) => request.get('/transfers/pending-allocations', { params }), + getPendingAllocationStats: () => request.get('/transfers/pending-allocations/stats'), + + // 昨日用户转账统计 + getDailyStats: () => request.get('/transfers/daily-stats') + }, + + // 系统设置 + system: { + getSettings: () => request.get('/system/settings'), + updateSettings: (category, data) => { + // 构造符合后端期望的数据格式 + const payload = { [category]: data } + return request.put('/system/settings', payload) + }, + getSystemInfo: () => request.get('/system/info'), + + }, + + // 风险管理 + risk: { + getUsers: () => request.get('/risk/users'), + getOverdueTransfers: () => request.get('/risk/overdue-transfers'), + getStats: () => request.get('/risk/stats'), + checkTimeouts: () => request.post('/risk/check-timeouts'), + blacklistUser: (userId, data) => request.post(`/risk/blacklist/${userId}`, data), + unblacklistUser: (userId) => request.post(`/risk/unblacklist/${userId}`) + }, + + // 匹配管理 + matching: { + getUnreasonableMatches: (params) => request.get('/admin/matching/unreasonable-matches', { params }), + getMatchingStats: () => request.get('/admin/matching/matching-stats'), + fixUnreasonableMatch: (allocationId, data) => request.post(`/admin/matching/fix-unreasonable-match/${allocationId}`, data), + fixAllUnreasonable: () => request.post('/admin/matching/fix-all-unreasonable'), + confirmAllocation: (allocationId) => request.post(`/admin/matching/confirm-allocation/${allocationId}`), + cancelAllocation: (allocationId) => request.post(`/admin/matching/cancel-allocation/${allocationId}`) + }, + + // 为了向后兼容,添加直接的 get、post 等方法 + get: (url, config) => request.get(url, config), + post: (url, data, config) => request.post(url, data, config), + put: (url, data, config) => request.put(url, data, config), + delete: (url, config) => request.delete(url, config) +} + +export default api \ No newline at end of file diff --git a/src/utils/config.js b/src/utils/config.js new file mode 100644 index 0000000..41cf9dd --- /dev/null +++ b/src/utils/config.js @@ -0,0 +1,68 @@ +// 环境配置 +const config = { + development: { + baseURL: 'http://localhost:3001', + uploadURL: 'http://localhost:3001/api/upload' + }, + production: { + baseURL: window.location.origin, + uploadURL: `${window.location.origin}/api/upload` + } +} + +// 获取当前环境 +const env = import.meta.env.MODE || 'development' + +// 导出当前环境的配置 +export default config[env] + +// 导出具体配置项 +export const { baseURL, uploadURL } = config[env] + +/** + * 获取完整的图片URL + * @param {string} imagePath - 图片路径 + * @returns {string} 完整的图片URL + */ +export const getImageUrl = (imagePath) => { + if (!imagePath) return '' + if (imagePath.startsWith('http')) return imagePath + + // 在开发环境下,使用代理路径 + if (env === 'development') { + // 如果路径已经包含uploads,直接使用 + if (imagePath.startsWith('/uploads/') || imagePath.startsWith('uploads/')) { + const cleanPath = imagePath.startsWith('/') ? imagePath : `/${imagePath}` + return cleanPath + } + // 否则添加uploads前缀 + return `/uploads/${imagePath}` + } + + // 生产环境下使用完整URL + const cleanBaseURL = baseURL.replace(/\/$/, '') + let cleanImagePath + + // 如果路径已经包含uploads,直接使用 + if (imagePath.startsWith('/uploads/') || imagePath.startsWith('uploads/')) { + cleanImagePath = imagePath.startsWith('/') ? imagePath : `/${imagePath}` + } else { + // 否则添加uploads前缀 + cleanImagePath = `/uploads/${imagePath}` + } + + return `${cleanBaseURL}${cleanImagePath}` +} + +/** + * 获取上传配置 + * @returns {object} 上传配置对象 + */ +export const getUploadConfig = () => { + return { + action: uploadURL, + headers: { + Authorization: `Bearer ${localStorage.getItem('admin_token')}` + } + } +} \ No newline at end of file diff --git a/src/views/404.vue b/src/views/404.vue new file mode 100644 index 0000000..737867d --- /dev/null +++ b/src/views/404.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/src/views/Agents.vue b/src/views/Agents.vue new file mode 100644 index 0000000..bdbf6c6 --- /dev/null +++ b/src/views/Agents.vue @@ -0,0 +1,1150 @@ + + + + + \ No newline at end of file diff --git a/src/views/DailyTransferStats.vue b/src/views/DailyTransferStats.vue new file mode 100644 index 0000000..25c473b --- /dev/null +++ b/src/views/DailyTransferStats.vue @@ -0,0 +1,519 @@ + + + + + \ No newline at end of file diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue new file mode 100644 index 0000000..ba3e6e3 --- /dev/null +++ b/src/views/Dashboard.vue @@ -0,0 +1,1538 @@ + + + + + \ No newline at end of file diff --git a/src/views/DatabaseMonitor.vue b/src/views/DatabaseMonitor.vue new file mode 100644 index 0000000..d130d6b --- /dev/null +++ b/src/views/DatabaseMonitor.vue @@ -0,0 +1,599 @@ + + + + + \ No newline at end of file diff --git a/src/views/Login.vue b/src/views/Login.vue new file mode 100644 index 0000000..e283969 --- /dev/null +++ b/src/views/Login.vue @@ -0,0 +1,362 @@ + + + + + \ No newline at end of file diff --git a/src/views/MatchingManagement.vue b/src/views/MatchingManagement.vue new file mode 100644 index 0000000..96c5aa7 --- /dev/null +++ b/src/views/MatchingManagement.vue @@ -0,0 +1,532 @@ + + + + + \ No newline at end of file diff --git a/src/views/Orders.vue b/src/views/Orders.vue new file mode 100644 index 0000000..eef60d8 --- /dev/null +++ b/src/views/Orders.vue @@ -0,0 +1,402 @@ + + + + + \ No newline at end of file diff --git a/src/views/Points.vue b/src/views/Points.vue new file mode 100644 index 0000000..104789b --- /dev/null +++ b/src/views/Points.vue @@ -0,0 +1,519 @@ + + + + + \ No newline at end of file diff --git a/src/views/ProductForm.vue b/src/views/ProductForm.vue new file mode 100644 index 0000000..004aa4a --- /dev/null +++ b/src/views/ProductForm.vue @@ -0,0 +1,296 @@ + + + + + \ No newline at end of file diff --git a/src/views/Products.vue b/src/views/Products.vue new file mode 100644 index 0000000..b4cdbc5 --- /dev/null +++ b/src/views/Products.vue @@ -0,0 +1,287 @@ + + + + + \ No newline at end of file diff --git a/src/views/Profile.vue b/src/views/Profile.vue new file mode 100644 index 0000000..2eed121 --- /dev/null +++ b/src/views/Profile.vue @@ -0,0 +1,805 @@ + + + + + \ No newline at end of file diff --git a/src/views/RegistrationCodes.vue b/src/views/RegistrationCodes.vue new file mode 100644 index 0000000..971a4ee --- /dev/null +++ b/src/views/RegistrationCodes.vue @@ -0,0 +1,630 @@ + + + + + \ No newline at end of file diff --git a/src/views/RiskManagement.vue b/src/views/RiskManagement.vue new file mode 100644 index 0000000..6d29018 --- /dev/null +++ b/src/views/RiskManagement.vue @@ -0,0 +1,450 @@ + + + + + \ No newline at end of file diff --git a/src/views/Settings.vue b/src/views/Settings.vue new file mode 100644 index 0000000..c50a9d7 --- /dev/null +++ b/src/views/Settings.vue @@ -0,0 +1,532 @@ + + + + + \ No newline at end of file diff --git a/src/views/Transfers.vue b/src/views/Transfers.vue new file mode 100644 index 0000000..467eeac --- /dev/null +++ b/src/views/Transfers.vue @@ -0,0 +1,1134 @@ + + + + + \ No newline at end of file diff --git a/src/views/UserAudit.vue b/src/views/UserAudit.vue new file mode 100644 index 0000000..62740de --- /dev/null +++ b/src/views/UserAudit.vue @@ -0,0 +1,504 @@ + + + + + \ No newline at end of file diff --git a/src/views/Users.vue b/src/views/Users.vue new file mode 100644 index 0000000..33dd0d4 --- /dev/null +++ b/src/views/Users.vue @@ -0,0 +1,1264 @@ + + + + + \ No newline at end of file diff --git a/src/views/Withdrawals.vue b/src/views/Withdrawals.vue new file mode 100644 index 0000000..44b494b --- /dev/null +++ b/src/views/Withdrawals.vue @@ -0,0 +1,687 @@ + + + + + \ No newline at end of file diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..4ffd7ec --- /dev/null +++ b/vite.config.js @@ -0,0 +1,45 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'path' + +// https://vitejs.dev/config/ +export default defineConfig({ + base: '/admin', + // base: '/', + plugins: [vue()], + resolve: { + alias: { + '@': resolve(__dirname, 'src') + } + }, + server: { + port: 5174, + proxy: { + '/api': { + target: 'http://localhost:3000', + changeOrigin: true + }, + '/admin': { + target: 'http://localhost:3000', + changeOrigin: true + }, + '/uploads': { + target: 'http://localhost:3000', + changeOrigin: true + } + } + }, + build: { + outDir: 'dist', + assetsDir: 'assets', + rollupOptions: { + output: { + manualChunks: { + vendor: ['vue', 'vue-router', 'pinia'], + elementPlus: ['element-plus'], + charts: ['echarts', 'vue-echarts'] + } + } + } + } +}) \ No newline at end of file