From 82dcc179681473bdad87000af0d3b57c59ad0c11 Mon Sep 17 00:00:00 2001 From: super Date: Sun, 28 Dec 2025 22:12:08 +0800 Subject: [PATCH] first commit --- .env | 6 + .env.development | 6 + .env.production | 6 + .gitignore | 24 + .vscode/extensions.json | 3 + README.md | 5 + index.html | 13 + package-lock.json | 2964 ++++++++++++++++++ package.json | 39 + public/vite.svg | 1 + src/App.vue | 17 + src/api/1panel.ts | 451 +++ src/api/index.ts | 6 + src/assets/vue.svg | 1 + src/auto-imports.d.ts | 91 + src/components.d.ts | 87 + src/components/ApprovalDrawer/index.vue | 336 ++ src/components/DuplicateFileModal.vue | 133 + src/components/DynamicMenu/index.vue | 85 + src/components/FlowEditor/index.vue | 694 ++++ src/components/HelloWorld.vue | 41 + src/components/ProjectUpload.vue | 221 ++ src/components/UploadCore.vue | 612 ++++ src/config/index.ts | 4 + src/config/project.ts | 274 ++ src/layouts/MainLayout.vue | 544 ++++ src/main.ts | 15 + src/mock/approval.ts | 458 +++ src/mock/finance.ts | 1071 +++++++ src/mock/index.ts | 7 + src/mock/projects.ts | 204 ++ src/mock/user.ts | 144 + src/router/index.ts | 256 ++ src/stores/index.ts | 12 + src/stores/project.ts | 292 ++ src/stores/user.ts | 117 + src/style.css | 48 + src/types/approval.ts | 199 ++ src/types/finance.ts | 946 ++++++ src/types/index.ts | 9 + src/types/response.ts | 33 + src/types/upload.ts | 28 + src/types/user.ts | 39 + src/utils/common.ts | 77 + src/utils/request.ts | 112 + src/views/app/IframePage.vue | 134 + src/views/error/404.vue | 33 + src/views/finance/accounts/index.vue | 671 ++++ src/views/finance/advanced-reports/index.vue | 898 ++++++ src/views/finance/budget/index.vue | 711 +++++ src/views/finance/expense/index.vue | 652 ++++ src/views/finance/import/index.vue | 608 ++++ src/views/finance/income/index.vue | 620 ++++ src/views/finance/invoice/index.vue | 147 + src/views/finance/overview/index.vue | 765 +++++ src/views/finance/reimbursement/index.vue | 800 +++++ src/views/finance/reports/index.vue | 776 +++++ src/views/finance/settlement/index.vue | 354 +++ src/views/login/index.vue | 428 +++ src/views/platform/certificates/index.vue | 1005 ++++++ src/views/platform/menus/index.vue | 518 +++ src/views/platform/projects/index.vue | 885 ++++++ src/views/platform/upload/index.vue | 592 ++++ src/views/settings/city/index.vue | 302 ++ src/views/settings/dict/index.vue | 498 +++ src/views/settings/index.vue | 82 + src/views/system/approval/index.vue | 520 +++ src/views/system/approval/instances.vue | 413 +++ tsconfig.app.json | 21 + tsconfig.json | 7 + tsconfig.node.json | 26 + vite.config.ts | 96 + 72 files changed, 23293 insertions(+) create mode 100644 .env create mode 100644 .env.development create mode 100644 .env.production create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 README.md create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/vite.svg create mode 100644 src/App.vue create mode 100644 src/api/1panel.ts create mode 100644 src/api/index.ts create mode 100644 src/assets/vue.svg create mode 100644 src/auto-imports.d.ts create mode 100644 src/components.d.ts create mode 100644 src/components/ApprovalDrawer/index.vue create mode 100644 src/components/DuplicateFileModal.vue create mode 100644 src/components/DynamicMenu/index.vue create mode 100644 src/components/FlowEditor/index.vue create mode 100644 src/components/HelloWorld.vue create mode 100644 src/components/ProjectUpload.vue create mode 100644 src/components/UploadCore.vue create mode 100644 src/config/index.ts create mode 100644 src/config/project.ts create mode 100644 src/layouts/MainLayout.vue create mode 100644 src/main.ts create mode 100644 src/mock/approval.ts create mode 100644 src/mock/finance.ts create mode 100644 src/mock/index.ts create mode 100644 src/mock/projects.ts create mode 100644 src/mock/user.ts create mode 100644 src/router/index.ts create mode 100644 src/stores/index.ts create mode 100644 src/stores/project.ts create mode 100644 src/stores/user.ts create mode 100644 src/style.css create mode 100644 src/types/approval.ts create mode 100644 src/types/finance.ts create mode 100644 src/types/index.ts create mode 100644 src/types/response.ts create mode 100644 src/types/upload.ts create mode 100644 src/types/user.ts create mode 100644 src/utils/common.ts create mode 100644 src/utils/request.ts create mode 100644 src/views/app/IframePage.vue create mode 100644 src/views/error/404.vue create mode 100644 src/views/finance/accounts/index.vue create mode 100644 src/views/finance/advanced-reports/index.vue create mode 100644 src/views/finance/budget/index.vue create mode 100644 src/views/finance/expense/index.vue create mode 100644 src/views/finance/import/index.vue create mode 100644 src/views/finance/income/index.vue create mode 100644 src/views/finance/invoice/index.vue create mode 100644 src/views/finance/overview/index.vue create mode 100644 src/views/finance/reimbursement/index.vue create mode 100644 src/views/finance/reports/index.vue create mode 100644 src/views/finance/settlement/index.vue create mode 100644 src/views/login/index.vue create mode 100644 src/views/platform/certificates/index.vue create mode 100644 src/views/platform/menus/index.vue create mode 100644 src/views/platform/projects/index.vue create mode 100644 src/views/platform/upload/index.vue create mode 100644 src/views/settings/city/index.vue create mode 100644 src/views/settings/dict/index.vue create mode 100644 src/views/settings/index.vue create mode 100644 src/views/system/approval/index.vue create mode 100644 src/views/system/approval/instances.vue create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.env b/.env new file mode 100644 index 0000000..3665510 --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +# 基础配置 +VITE_APP_TITLE=楠溪屿后台管理系统 + +# API基础URL +VITE_API_BASE_URL=/api + diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..89a512f --- /dev/null +++ b/.env.development @@ -0,0 +1,6 @@ +# 开发环境配置 +VITE_APP_TITLE=楠溪屿后台管理系统(开发环境) + +# API基础URL - 开发环境 +VITE_API_BASE_URL=http://localhost:8080/api + diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..b8cb314 --- /dev/null +++ b/.env.production @@ -0,0 +1,6 @@ +# 生产环境配置 +VITE_APP_TITLE=楠溪屿后台管理系统 + +# API基础URL - 生产环境 +VITE_API_BASE_URL=/api + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..33895ab --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e2af624 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2964 @@ +{ + "name": "nanxiisletadmin", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nanxiisletadmin", + "version": "0.0.0", + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "@types/echarts": "^4.9.22", + "@vue-flow/background": "^1.3.2", + "@vue-flow/controls": "^1.1.3", + "@vue-flow/core": "^1.48.0", + "@vue-flow/minimap": "^1.5.4", + "ant-design-vue": "^4.2.6", + "axios": "^1.13.2", + "echarts": "^6.0.0", + "pinia": "^3.0.4", + "spark-md5": "^3.0.2", + "vue": "^3.5.24", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@iconify/json": "^2.2.412", + "@types/node": "^24.10.1", + "@types/spark-md5": "^3.0.5", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.8.1", + "typescript": "~5.9.3", + "unplugin-auto-import": "^20.3.0", + "unplugin-icons": "^22.5.0", + "unplugin-vue-components": "^30.0.0", + "vite": "^7.2.4", + "vue-tsc": "^3.1.4" + } + }, + "node_modules/@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/icons-vue": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz", + "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1" + }, + "peerDependencies": { + "vue": ">=3.0.3" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@iconify/json": { + "version": "2.2.412", + "resolved": "https://registry.npmmirror.com/@iconify/json/-/json-2.2.412.tgz", + "integrity": "sha512-WV6kFgS6mfR6BdBVZ4x2ctsZy2hzQEacvGLSW439yizXrmAszkAJXAl7ytZ3VI0KL2TylUluQGc4ygHsQDrIww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@iconify/types": "*", + "pathe": "^2.0.3" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.50", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.50.tgz", + "integrity": "sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@simonwep/pickr": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/@simonwep/pickr/-/pickr-1.8.2.tgz", + "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==", + "license": "MIT", + "dependencies": { + "core-js": "^3.15.1", + "nanopop": "^2.1.0" + } + }, + "node_modules/@types/echarts": { + "version": "4.9.22", + "resolved": "https://registry.npmmirror.com/@types/echarts/-/echarts-4.9.22.tgz", + "integrity": "sha512-7Fo6XdWpoi8jxkwP7BARUOM7riq8bMhmsCtSG8gzUcJmFhLo387tihoBYS/y5j7jl3PENT5RxeWZdN9RiwO7HQ==", + "license": "MIT", + "dependencies": { + "@types/zrender": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.1", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.10.1.tgz", + "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/spark-md5": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types/spark-md5/-/spark-md5-3.0.5.tgz", + "integrity": "sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", + "license": "MIT" + }, + "node_modules/@types/zrender": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/@types/zrender/-/zrender-4.0.6.tgz", + "integrity": "sha512-1jZ9bJn2BsfmYFPBHtl5o3uV+ILejAtGrDcYSpT4qaVKEI/0YY+arw3XHU04Ebd8Nca3SQ7uNcLaqiL+tTFVMg==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.2.tgz", + "integrity": "sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.50" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue-flow/background": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/@vue-flow/background/-/background-1.3.2.tgz", + "integrity": "sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==", + "license": "MIT", + "peerDependencies": { + "@vue-flow/core": "^1.23.0", + "vue": "^3.3.0" + } + }, + "node_modules/@vue-flow/controls": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@vue-flow/controls/-/controls-1.1.3.tgz", + "integrity": "sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==", + "license": "MIT", + "peerDependencies": { + "@vue-flow/core": "^1.23.0", + "vue": "^3.3.0" + } + }, + "node_modules/@vue-flow/core": { + "version": "1.48.0", + "resolved": "https://registry.npmmirror.com/@vue-flow/core/-/core-1.48.0.tgz", + "integrity": "sha512-keW9HGaEZEe4SKYtrzp5E+qSGJ5/z+9i2yRDtCr3o72IUnS0Ns1qQNsIbGGz0ygpKzg6LdtbVLWeYAvl3dzLQA==", + "license": "MIT", + "dependencies": { + "@vueuse/core": "^10.5.0", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + }, + "peerDependencies": { + "vue": "^3.3.0" + } + }, + "node_modules/@vue-flow/minimap": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@vue-flow/minimap/-/minimap-1.5.4.tgz", + "integrity": "sha512-l4C+XTAXnRxsRpUdN7cAVFBennC1sVRzq4bDSpVK+ag7tdMczAnhFYGgbLkUw3v3sY6gokyWwMl8CDonp8eB2g==", + "license": "MIT", + "dependencies": { + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + }, + "peerDependencies": { + "@vue-flow/core": "^1.23.0", + "vue": "^3.3.0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.25.tgz", + "integrity": "sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.25", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.25.tgz", + "integrity": "sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.25.tgz", + "integrity": "sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.25", + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.25.tgz", + "integrity": "sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/devtools-api": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz", + "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.9" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", + "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.9", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.9", + "resolved": "https://registry.npmmirror.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", + "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/language-core": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-3.1.5.tgz", + "integrity": "sha512-FMcqyzWN+sYBeqRMWPGT2QY0mUasZMVIuHvmb5NT3eeqPrbHBYtCP8JWEUCDCgM+Zr62uuWY/qoeBrPrzfa78w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.0.0", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.25.tgz", + "integrity": "sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.25.tgz", + "integrity": "sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/shared": "3.5.25" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.25.tgz", + "integrity": "sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.25", + "@vue/runtime-core": "3.5.25", + "@vue/shared": "3.5.25", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.25.tgz", + "integrity": "sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "vue": "3.5.25" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.25.tgz", + "integrity": "sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": "5.x", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core": { + "version": "10.11.1", + "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz", + "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.1", + "@vueuse/shared": "10.11.1", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "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/@vueuse/metadata": { + "version": "10.11.1", + "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz", + "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "10.11.1", + "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz", + "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==", + "license": "MIT", + "dependencies": { + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "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/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alien-signals": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/alien-signals/-/alien-signals-3.1.1.tgz", + "integrity": "sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ant-design-vue": { + "version": "4.2.6", + "resolved": "https://registry.npmmirror.com/ant-design-vue/-/ant-design-vue-4.2.6.tgz", + "integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-vue": "^7.0.0", + "@babel/runtime": "^7.10.5", + "@ctrl/tinycolor": "^3.5.0", + "@emotion/hash": "^0.9.0", + "@emotion/unitless": "^0.8.0", + "@simonwep/pickr": "~1.8.0", + "array-tree-filter": "^2.1.0", + "async-validator": "^4.0.0", + "csstype": "^3.1.1", + "dayjs": "^1.10.5", + "dom-align": "^1.12.1", + "dom-scroll-into-view": "^2.0.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.15", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.25", + "shallow-equal": "^1.0.0", + "stylis": "^4.1.3", + "throttle-debounce": "^5.0.0", + "vue-types": "^3.0.0", + "warning": "^4.0.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design-vue" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, + "node_modules/array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==", + "license": "MIT" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/birpc": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.8.0.tgz", + "integrity": "sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/copy-anything/-/copy-anything-4.0.5.tgz", + "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", + "license": "MIT", + "dependencies": { + "is-what": "^5.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-align": { + "version": "1.12.4", + "resolved": "https://registry.npmmirror.com/dom-align/-/dom-align-1.12.4.tgz", + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==", + "license": "MIT" + }, + "node_modules/dom-scroll-into-view": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz", + "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/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": "6.0.0", + "resolved": "https://registry.npmmirror.com/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "2.3.0", + "zrender": "6.0.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-what": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/is-what/-/is-what-5.5.0.tgz", + "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanopop": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/nanopop/-/nanopop-2.4.2.tgz", + "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==", + "license": "MIT" + }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz", + "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.7" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.5.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmmirror.com/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.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "license": "(WTFPL OR MIT)" + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmmirror.com/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmmirror.com/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unimport": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/unimport/-/unimport-5.5.0.tgz", + "integrity": "sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.19", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "pkg-types": "^2.3.0", + "scule": "^1.3.0", + "strip-literal": "^3.1.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.10", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unimport/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/unplugin": { + "version": "2.3.11", + "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz", + "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin-auto-import": { + "version": "20.3.0", + "resolved": "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-20.3.0.tgz", + "integrity": "sha512-RcSEQiVv7g0mLMMXibYVKk8mpteKxvyffGuDKqZZiFr7Oq3PB1HwgHdK5O7H4AzbhzHoVKG0NnMnsk/1HIVYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "local-pkg": "^1.1.2", + "magic-string": "^0.30.21", + "picomatch": "^4.0.3", + "unimport": "^5.5.0", + "unplugin": "^2.3.11", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@nuxt/kit": "^4.0.0", + "@vueuse/core": "*" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "@vueuse/core": { + "optional": true + } + } + }, + "node_modules/unplugin-icons": { + "version": "22.5.0", + "resolved": "https://registry.npmmirror.com/unplugin-icons/-/unplugin-icons-22.5.0.tgz", + "integrity": "sha512-MBlMtT5RuMYZy4TZgqUL2OTtOdTUVsS1Mhj6G1pEzMlFJlEnq6mhUfoIt45gBWxHcsOdXJDWLg3pRZ+YmvAVWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/utils": "^3.0.2", + "debug": "^4.4.3", + "local-pkg": "^1.1.2", + "unplugin": "^2.3.10" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@svgr/core": ">=7.0.0", + "@svgx/core": "^1.0.1", + "@vue/compiler-sfc": "^3.0.2 || ^2.7.0", + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0", + "vue-template-compiler": "^2.6.12", + "vue-template-es2015-compiler": "^1.9.0" + }, + "peerDependenciesMeta": { + "@svgr/core": { + "optional": true + }, + "@svgx/core": { + "optional": true + }, + "@vue/compiler-sfc": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue-template-compiler": { + "optional": true + }, + "vue-template-es2015-compiler": { + "optional": true + } + } + }, + "node_modules/unplugin-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz", + "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-vue-components": { + "version": "30.0.0", + "resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz", + "integrity": "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "debug": "^4.4.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.19", + "mlly": "^1.8.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.10", + "unplugin-utils": "^0.3.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@babel/parser": "^7.15.8", + "@nuxt/kit": "^3.2.2 || ^4.0.0", + "vue": "2 || 3" + }, + "peerDependenciesMeta": { + "@babel/parser": { + "optional": true + }, + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite": { + "version": "7.2.4", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.2.4.tgz", + "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.25", + "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz", + "integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.25", + "@vue/compiler-sfc": "3.5.25", + "@vue/runtime-dom": "3.5.25", + "@vue/server-renderer": "3.5.25", + "@vue/shared": "3.5.25" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-3.1.5.tgz", + "integrity": "sha512-L/G9IUjOWhBU0yun89rv8fKqmKC+T0HfhrFjlIml71WpfBv9eb4E9Bev8FMbyueBIU9vxQqbd+oOsVcDa5amGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.1.5" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vue-types": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/vue-types/-/vue-types-3.0.2.tgz", + "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==", + "license": "MIT", + "dependencies": { + "is-plain-object": "3.0.1" + }, + "engines": { + "node": ">=10.15.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/zrender": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "license": "BSD-3-Clause", + "dependencies": { + "tslib": "2.3.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..4de127f --- /dev/null +++ b/package.json @@ -0,0 +1,39 @@ +{ + "name": "nanxiisletadmin", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "@types/echarts": "^4.9.22", + "@vue-flow/background": "^1.3.2", + "@vue-flow/controls": "^1.1.3", + "@vue-flow/core": "^1.48.0", + "@vue-flow/minimap": "^1.5.4", + "ant-design-vue": "^4.2.6", + "axios": "^1.13.2", + "echarts": "^6.0.0", + "pinia": "^3.0.4", + "spark-md5": "^3.0.2", + "vue": "^3.5.24", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@iconify/json": "^2.2.412", + "@types/node": "^24.10.1", + "@types/spark-md5": "^3.0.5", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.8.1", + "typescript": "~5.9.3", + "unplugin-auto-import": "^20.3.0", + "unplugin-icons": "^22.5.0", + "unplugin-vue-components": "^30.0.0", + "vite": "^7.2.4", + "vue-tsc": "^3.1.4" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..1b9c701 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,17 @@ + + + + + diff --git a/src/api/1panel.ts b/src/api/1panel.ts new file mode 100644 index 0000000..39258bf --- /dev/null +++ b/src/api/1panel.ts @@ -0,0 +1,451 @@ +/** + * 1Panel API 接口 + * + * 1Panel API 认证说明: + * - Token = md5('1panel' + API-Key + UnixTimestamp) + * - 请求头需要携带: + * - 1Panel-Token: 生成的Token值 + * - 1Panel-Timestamp: 当前时间戳(秒级) + */ +import axios, { type AxiosInstance } from 'axios' +import { message } from 'ant-design-vue' +import SparkMD5 from 'spark-md5' + +// 1Panel API 配置(可动态修改) +const PANEL_CONFIG = { + // 服务器ID(用于代理路径) + serverId: 'server1', + // 服务器地址(不含协议和路径) + serverAddress: '47.109.57.58:42588', + // 完整的 baseURL(开发环境使用代理路径,生产环境直接请求) + baseURL: import.meta.env.DEV ? '/1panel-api/server1' : 'http://47.109.57.58:42588/api/v2', + // API密钥 - 可动态切换 + apiKey: import.meta.env.VITE_1PANEL_API_KEY || '' +} + +/** + * 服务器配置接口 + */ +export interface PanelServerConfig { + id: string // 服务器ID,用于代理路径 + address: string // 服务器地址 + apiKey: string // API密钥 +} + +/** + * 切换 1Panel 服务器配置 + * @param config 服务器配置 + */ +export function setPanelServer(config: PanelServerConfig): void { + PANEL_CONFIG.serverId = config.id + PANEL_CONFIG.serverAddress = config.address + PANEL_CONFIG.apiKey = config.apiKey + + // 开发环境使用代理路径,生产环境直接请求 + if (import.meta.env.DEV) { + PANEL_CONFIG.baseURL = `/1panel-api/${config.id}` + } else { + PANEL_CONFIG.baseURL = `http://${config.address}/api/v2` + } + + // 更新 axios 实例的 baseURL + panelService.defaults.baseURL = PANEL_CONFIG.baseURL + + console.log('[1Panel API] 服务器配置已更新:', { + serverId: PANEL_CONFIG.serverId, + serverAddress: PANEL_CONFIG.serverAddress, + baseURL: PANEL_CONFIG.baseURL, + apiKeyConfigured: !!PANEL_CONFIG.apiKey, + isDev: import.meta.env.DEV + }) +} + +/** + * 获取当前 1Panel 服务器配置 + */ +export function getPanelConfig() { + return { + serverId: PANEL_CONFIG.serverId, + serverAddress: PANEL_CONFIG.serverAddress, + baseURL: PANEL_CONFIG.baseURL, + apiKeyConfigured: !!PANEL_CONFIG.apiKey + } +} + +/** + * 生成 MD5 哈希值 + * @param str 要加密的字符串 + * @returns MD5哈希值 + */ +function md5(str: string): string { + return SparkMD5.hash(str) +} + +/** + * 生成 1Panel Token + * @param timestamp 时间戳(秒级) + * @returns Token字符串 + */ +function generateToken(timestamp: number): string { + const tokenString = `1panel${PANEL_CONFIG.apiKey}${timestamp}` + return md5(tokenString) +} + +/** + * 获取当前时间戳(秒级) + * @returns 时间戳 + */ +function getTimestamp(): number { + return Math.floor(Date.now() / 1000) +} + +// 创建 1Panel 专用的 axios 实例 +const panelService: AxiosInstance = axios.create({ + baseURL: PANEL_CONFIG.baseURL, + timeout: 30000, + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 - 添加 1Panel 认证头 +panelService.interceptors.request.use( + (config) => { + const timestamp = getTimestamp() + const token = generateToken(timestamp) + + // 调试日志 + console.log('[1Panel API] 请求调试信息:', { + url: config.url, + apiKeyConfigured: !!PANEL_CONFIG.apiKey, + apiKeyLength: PANEL_CONFIG.apiKey?.length || 0, + timestamp, + tokenPreview: token.substring(0, 8) + '...' + }) + + // 添加 1Panel 认证头 + config.headers['1Panel-Token'] = token + config.headers['1Panel-Timestamp'] = String(timestamp) + + return config + }, + (error) => { + console.error('1Panel 请求错误:', error) + return Promise.reject(error) + } +) + +// 响应拦截器 +panelService.interceptors.response.use( + (response) => { + return response + }, + (error) => { + console.error('1Panel 响应错误:', error) + + let errorMessage = '1Panel API 请求失败' + if (error.response) { + const { status, data } = error.response + switch (status) { + case 401: + errorMessage = 'API 接口密钥错误或已过期' + break + case 403: + errorMessage = '拒绝访问,请检查权限配置' + break + case 404: + errorMessage = '接口不存在' + break + case 500: + errorMessage = '1Panel 服务器错误' + break + default: + errorMessage = data?.message || `请求失败(${status})` + } + } else if (error.code === 'ECONNABORTED') { + errorMessage = '请求超时' + } else if (error.code === 'ERR_NETWORK') { + errorMessage = '网络连接失败,请检查服务器地址' + } + + message.error(errorMessage) + return Promise.reject(error) + } +) + +// ==================== API 接口定义 ==================== + +/** + * 网站信息类型 + */ +export interface Website { + id: number + primaryDomain: string + alias: string + type: string + remark: string + status: string + expireDate: string + sitePath: string + appName: string + runtimeName: string + sslExpireDate: string + sslStatus: string + protocol: string + createdAt: string + updatedAt: string +} + +/** + * 网站列表查询参数 + */ +export interface WebsiteSearchParams { + page: number + pageSize: number + name?: string + websiteGroupId?: number + orderBy?: string + order?: string +} + +/** + * 分页响应 + */ +export interface PageResponse { + items: T[] + total: number +} + +/** + * 1Panel 标准响应 + */ +export interface PanelResponse { + code: number + message: string + data: T +} + +/** + * 获取网站列表(搜索接口) + * @param params 查询参数 + */ +export async function searchWebsites(params: WebsiteSearchParams): Promise> { + const response = await panelService.post>>('/websites/search', { + ...params, + name: params.name || '', + orderBy: params.orderBy || 'created_at', + order: params.order || 'descending' + }) + return response.data.data +} + +/** + * 获取所有网站列表(简化调用,用于下拉选择) + * 使用 /websites/list 接口,无需参数 + */ +export async function getAllWebsites(): Promise { + const response = await panelService.get>('/websites/list') + return response.data.data || [] +} + +// ==================== 证书相关 API ==================== + +/** + * SSL证书信息类型 + */ +export interface SSLCertificate { + id: number + primaryDomain: string + domains: string + type: string + provider: string + organization: string + status: string + startDate: string + expireDate: string + autoRenew: boolean + acmeAccountId: number + dnsAccountId: number + description: string + createdAt: string + updatedAt: string +} + +/** + * 证书列表查询参数 + */ +export interface SSLSearchParams { + page: number + pageSize: number + info?: string +} + +/** + * 获取证书列表 + * @param params 查询参数 + */ +export async function getSSLCertificates(params: SSLSearchParams): Promise> { + const response = await panelService.post>>('/websites/ssl/search', { + ...params, + orderBy: 'created_at', + order: 'descending' + }) + return response.data.data +} + +/** + * 根据 Acme 账户ID 获取证书列表 + * @param acmeAccountID Acme账户ID + */ +export async function getSSLCertificatesByAcmeAccount(acmeAccountID: number | string): Promise { + const response = await panelService.post>>('/websites/ssl/search', { + page: 1, + pageSize: 100, + acmeAccountID: String(acmeAccountID), + orderBy: 'created_at', + order: 'descending' + }) + return response.data.data?.items || [] +} + +/** + * 根据域名查找匹配的证书 + * @param domain 域名 + * @param acmeAccountID Acme账户ID + * @returns 匹配的证书或 null + */ +export async function findCertificateByDomain(domain: string, acmeAccountID: number | string = 1): Promise { + const certificates = await getSSLCertificatesByAcmeAccount(acmeAccountID) + + // 查找主域名或其他域名匹配的证书 + const matchedCert = certificates.find(cert => { + // 主域名匹配 + if (cert.primaryDomain === domain) return true + // 检查其他域名 + if (cert.domains) { + const otherDomains = cert.domains.split(',').map(d => d.trim()) + if (otherDomains.includes(domain)) return true + } + return false + }) + + return matchedCert || null +} + +/** + * Acme账户类型 + */ +export interface AcmeAccount { + id: number + email: string + url: string + type: string + createdAt: string +} + +/** + * 获取 ACME 账户列表 + */ +export async function getAcmeAccounts(): Promise> { + const response = await panelService.post>>('/websites/acme/search', { + page: 1, + pageSize: 100, + orderBy: 'created_at', + order: 'descending' + }) + return response.data.data +} + +/** + * DNS账户类型 + */ +export interface DnsAccount { + id: number + name: string + type: string + authorization: string + createdAt: string +} + +/** + * 获取 DNS 账户列表 + */ +export async function getDnsAccounts(): Promise> { + const response = await panelService.post>>('/websites/dns/search', { + page: 1, + pageSize: 100, + orderBy: 'created_at', + order: 'descending' + }) + return response.data.data +} + +/** + * 申请证书参数 + */ +export interface ApplySSLParams { + primaryDomain: string + otherDomains: string + provider: string + acmeAccountId: number + dnsAccountId?: number + autoRenew: boolean + keyType: string + apply: boolean + pushDir?: boolean + dir?: string + id?: number + websiteId?: number + execShell?: boolean + shell?: string + skipDNS?: boolean + disableCNAME?: boolean + nameserverId?: number +} + +/** + * 申请SSL证书 + * @param params 申请参数 + */ +export async function applySSLCertificate(params: ApplySSLParams): Promise { + await panelService.post('/websites/ssl', params) +} + +/** + * 删除SSL证书 + * @param id 证书ID + */ +export async function deleteSSLCertificate(id: number): Promise { + await panelService.post('/websites/ssl/del', { id }) +} + +/** + * 获取证书详情 + * @param id 证书ID + */ +export async function getSSLCertificateDetail(id: number): Promise { + const response = await panelService.get>(`/websites/ssl/${id}`) + return response.data.data +} + +/** + * 更新证书设置 + */ +export interface UpdateSSLParams { + id: number + autoRenew: boolean + description?: string + execShell?: boolean + shell?: string +} + +/** + * 更新证书设置 + * @param params 更新参数 + */ +export async function updateSSLCertificate(params: UpdateSSLParams): Promise { + await panelService.post('/websites/ssl/update', params) +} + +// 导出 panelService 实例供其他地方使用 +export default panelService diff --git a/src/api/index.ts b/src/api/index.ts new file mode 100644 index 0000000..04bb3ea --- /dev/null +++ b/src/api/index.ts @@ -0,0 +1,6 @@ +/** + * API 模块导出 + */ + +// 1Panel API +export * from './1panel' diff --git a/src/assets/vue.svg b/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts new file mode 100644 index 0000000..71e70ef --- /dev/null +++ b/src/auto-imports.d.ts @@ -0,0 +1,91 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +// biome-ignore lint: disable +export {} +declare global { + const EffectScope: typeof import('vue').EffectScope + const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate + const axios: typeof import('axios').default + const computed: typeof import('vue').computed + const createApp: typeof import('vue').createApp + const createPinia: typeof import('pinia').createPinia + const customRef: typeof import('vue').customRef + const defineAsyncComponent: typeof import('vue').defineAsyncComponent + const defineComponent: typeof import('vue').defineComponent + const defineStore: typeof import('pinia').defineStore + const effectScope: typeof import('vue').effectScope + const getActivePinia: typeof import('pinia').getActivePinia + const getCurrentInstance: typeof import('vue').getCurrentInstance + const getCurrentScope: typeof import('vue').getCurrentScope + const getCurrentWatcher: typeof import('vue').getCurrentWatcher + const h: typeof import('vue').h + const inject: typeof import('vue').inject + const isProxy: typeof import('vue').isProxy + const isReactive: typeof import('vue').isReactive + const isReadonly: typeof import('vue').isReadonly + const isRef: typeof import('vue').isRef + const isShallow: typeof import('vue').isShallow + const mapActions: typeof import('pinia').mapActions + const mapGetters: typeof import('pinia').mapGetters + const mapState: typeof import('pinia').mapState + const mapStores: typeof import('pinia').mapStores + const mapWritableState: typeof import('pinia').mapWritableState + const markRaw: typeof import('vue').markRaw + const nextTick: typeof import('vue').nextTick + const onActivated: typeof import('vue').onActivated + const onBeforeMount: typeof import('vue').onBeforeMount + const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave + const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate + const onBeforeUnmount: typeof import('vue').onBeforeUnmount + const onBeforeUpdate: typeof import('vue').onBeforeUpdate + const onDeactivated: typeof import('vue').onDeactivated + const onErrorCaptured: typeof import('vue').onErrorCaptured + const onMounted: typeof import('vue').onMounted + const onRenderTracked: typeof import('vue').onRenderTracked + const onRenderTriggered: typeof import('vue').onRenderTriggered + const onScopeDispose: typeof import('vue').onScopeDispose + const onServerPrefetch: typeof import('vue').onServerPrefetch + const onUnmounted: typeof import('vue').onUnmounted + const onUpdated: typeof import('vue').onUpdated + const onWatcherCleanup: typeof import('vue').onWatcherCleanup + const provide: typeof import('vue').provide + const reactive: typeof import('vue').reactive + const readonly: typeof import('vue').readonly + const ref: typeof import('vue').ref + const resolveComponent: typeof import('vue').resolveComponent + const setActivePinia: typeof import('pinia').setActivePinia + const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix + const shallowReactive: typeof import('vue').shallowReactive + const shallowReadonly: typeof import('vue').shallowReadonly + const shallowRef: typeof import('vue').shallowRef + const storeToRefs: typeof import('pinia').storeToRefs + const toRaw: typeof import('vue').toRaw + const toRef: typeof import('vue').toRef + const toRefs: typeof import('vue').toRefs + const toValue: typeof import('vue').toValue + const triggerRef: typeof import('vue').triggerRef + const unref: typeof import('vue').unref + const useAttrs: typeof import('vue').useAttrs + const useCssModule: typeof import('vue').useCssModule + const useCssVars: typeof import('vue').useCssVars + const useId: typeof import('vue').useId + const useLink: typeof import('vue-router').useLink + const useModel: typeof import('vue').useModel + const useRoute: typeof import('vue-router').useRoute + const useRouter: typeof import('vue-router').useRouter + const useSlots: typeof import('vue').useSlots + const useTemplateRef: typeof import('vue').useTemplateRef + const watch: typeof import('vue').watch + const watchEffect: typeof import('vue').watchEffect + const watchPostEffect: typeof import('vue').watchPostEffect + const watchSyncEffect: typeof import('vue').watchSyncEffect +} +// for type re-export +declare global { + // @ts-ignore + export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + import('vue') +} diff --git a/src/components.d.ts b/src/components.d.ts new file mode 100644 index 0000000..a05a025 --- /dev/null +++ b/src/components.d.ts @@ -0,0 +1,87 @@ +/* eslint-disable */ +// @ts-nocheck +// biome-ignore lint: disable +// oxlint-disable +// ------ +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 + +export {} + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + AAlert: typeof import('ant-design-vue/es')['Alert'] + AAvatar: typeof import('ant-design-vue/es')['Avatar'] + ABadge: typeof import('ant-design-vue/es')['Badge'] + ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb'] + ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem'] + AButton: typeof import('ant-design-vue/es')['Button'] + ACard: typeof import('ant-design-vue/es')['Card'] + ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] + ACol: typeof import('ant-design-vue/es')['Col'] + ADatePicker: typeof import('ant-design-vue/es')['DatePicker'] + ADescriptions: typeof import('ant-design-vue/es')['Descriptions'] + ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem'] + ADivider: typeof import('ant-design-vue/es')['Divider'] + ADrawer: typeof import('ant-design-vue/es')['Drawer'] + ADropdown: typeof import('ant-design-vue/es')['Dropdown'] + AEmpty: typeof import('ant-design-vue/es')['Empty'] + AForm: typeof import('ant-design-vue/es')['Form'] + AFormItem: typeof import('ant-design-vue/es')['FormItem'] + AInput: typeof import('ant-design-vue/es')['Input'] + AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] + AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] + AInputSearch: typeof import('ant-design-vue/es')['InputSearch'] + ALayout: typeof import('ant-design-vue/es')['Layout'] + ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent'] + ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader'] + ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider'] + AList: typeof import('ant-design-vue/es')['List'] + AListItem: typeof import('ant-design-vue/es')['ListItem'] + AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta'] + AMenu: typeof import('ant-design-vue/es')['Menu'] + AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider'] + AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] + AMenuItemGroup: typeof import('ant-design-vue/es')['MenuItemGroup'] + AModal: typeof import('ant-design-vue/es')['Modal'] + APageHeader: typeof import('ant-design-vue/es')['PageHeader'] + APagination: typeof import('ant-design-vue/es')['Pagination'] + APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] + ApprovalDrawer: typeof import('./components/ApprovalDrawer/index.vue')['default'] + AProgress: typeof import('ant-design-vue/es')['Progress'] + ARadio: typeof import('ant-design-vue/es')['Radio'] + ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] + ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] + ARangePicker: typeof import('ant-design-vue/es')['RangePicker'] + AResult: typeof import('ant-design-vue/es')['Result'] + ARow: typeof import('ant-design-vue/es')['Row'] + ASelect: typeof import('ant-design-vue/es')['Select'] + ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] + ASpace: typeof import('ant-design-vue/es')['Space'] + ASpin: typeof import('ant-design-vue/es')['Spin'] + AStatistic: typeof import('ant-design-vue/es')['Statistic'] + AStep: typeof import('ant-design-vue/es')['Step'] + ASteps: typeof import('ant-design-vue/es')['Steps'] + ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] + ASwitch: typeof import('ant-design-vue/es')['Switch'] + ATable: typeof import('ant-design-vue/es')['Table'] + ATabPane: typeof import('ant-design-vue/es')['TabPane'] + ATabs: typeof import('ant-design-vue/es')['Tabs'] + ATag: typeof import('ant-design-vue/es')['Tag'] + ATextarea: typeof import('ant-design-vue/es')['Textarea'] + ATimeline: typeof import('ant-design-vue/es')['Timeline'] + ATimelineItem: typeof import('ant-design-vue/es')['TimelineItem'] + ATooltip: typeof import('ant-design-vue/es')['Tooltip'] + ATree: typeof import('ant-design-vue/es')['Tree'] + AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger'] + DuplicateFileModal: typeof import('./components/DuplicateFileModal.vue')['default'] + DynamicMenu: typeof import('./components/DynamicMenu/index.vue')['default'] + FlowEditor: typeof import('./components/FlowEditor/index.vue')['default'] + HelloWorld: typeof import('./components/HelloWorld.vue')['default'] + ProjectUpload: typeof import('./components/ProjectUpload.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + UploadCore: typeof import('./components/UploadCore.vue')['default'] + } +} diff --git a/src/components/ApprovalDrawer/index.vue b/src/components/ApprovalDrawer/index.vue new file mode 100644 index 0000000..fa4f037 --- /dev/null +++ b/src/components/ApprovalDrawer/index.vue @@ -0,0 +1,336 @@ + + + + + diff --git a/src/components/DuplicateFileModal.vue b/src/components/DuplicateFileModal.vue new file mode 100644 index 0000000..8788231 --- /dev/null +++ b/src/components/DuplicateFileModal.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/src/components/DynamicMenu/index.vue b/src/components/DynamicMenu/index.vue new file mode 100644 index 0000000..2e2cede --- /dev/null +++ b/src/components/DynamicMenu/index.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/components/FlowEditor/index.vue b/src/components/FlowEditor/index.vue new file mode 100644 index 0000000..109dac5 --- /dev/null +++ b/src/components/FlowEditor/index.vue @@ -0,0 +1,694 @@ + + + + + + + + diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue new file mode 100644 index 0000000..b58e52b --- /dev/null +++ b/src/components/HelloWorld.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/components/ProjectUpload.vue b/src/components/ProjectUpload.vue new file mode 100644 index 0000000..422caff --- /dev/null +++ b/src/components/ProjectUpload.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/src/components/UploadCore.vue b/src/components/UploadCore.vue new file mode 100644 index 0000000..f1f10a0 --- /dev/null +++ b/src/components/UploadCore.vue @@ -0,0 +1,612 @@ + + + + + diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000..c4019a9 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,4 @@ +/** + * 配置模块统一导出 + */ +export * from './project' diff --git a/src/config/project.ts b/src/config/project.ts new file mode 100644 index 0000000..c52e57f --- /dev/null +++ b/src/config/project.ts @@ -0,0 +1,274 @@ +/** + * 项目配置系统 - 框架版 + * + * 该文件用于配置管理后台的框架模块 + * - 系统管理 + * - 财务管理 + * - 平台管理 + */ + +import type { Component } from 'vue' +import { + PayCircleOutlined, + SettingOutlined, + AppstoreOutlined +} from '@ant-design/icons-vue' + +// 项目标识类型 +export type ProjectId = 'framework' | 'default' + +// 当前项目配置 +export const CURRENT_PROJECT: ProjectId = 'framework' + +// 菜单项类型 +export interface MenuItem { + key: string + label: string + icon?: Component + path?: string + children?: MenuItem[] +} + +// 模块类型 +export type ModuleType = 'framework' + +// 模块配置 +export interface ModuleConfig { + id: string + name: string + type: ModuleType + icon?: Component + menus: MenuItem[] + routes: RouteConfig[] +} + +// 路由配置 +export interface RouteConfig { + path: string + name: string + component: string + title: string +} + +// ==================== 框架模块 ==================== + +// 系统管理模块 +export const systemModule: ModuleConfig = { + id: 'system', + name: '系统管理', + type: 'framework', + icon: SettingOutlined, + menus: [ + { + key: 'system', + label: '系统管理', + children: [ + { key: 'settings', label: '系统设置', path: '/settings' }, + { key: 'dict', label: '字典管理', path: '/settings/dict' }, + { key: 'city', label: '城市管理', path: '/settings/city' }, + { key: 'approval-flow', label: '审批流程', path: '/system/approval' }, + { key: 'approval-instances', label: '审批记录', path: '/system/approval-instances' } + ] + } + ], + routes: [ + { path: 'settings', name: 'Settings', component: '@/views/settings/index.vue', title: '系统设置' }, + { path: 'settings/dict', name: 'Dict', component: '@/views/settings/dict/index.vue', title: '字典管理' }, + { path: 'settings/city', name: 'City', component: '@/views/settings/city/index.vue', title: '城市管理' }, + { path: 'system/approval', name: 'ApprovalFlow', component: '@/views/system/approval/index.vue', title: '审批流程' }, + { path: 'system/approval-instances', name: 'ApprovalInstances', component: '@/views/system/approval-instances/index.vue', title: '审批记录' } + ] +} + +// 财务管理模块 +export const financeModule: ModuleConfig = { + id: 'finance', + name: '财务管理', + type: 'framework', + icon: PayCircleOutlined, + menus: [ + { + key: 'finance', + label: '财务管理', + children: [ + { key: 'finance-overview', label: '财务总览', path: '/finance/overview' }, + { key: 'income', label: '收入管理', path: '/finance/income' }, + { key: 'expense', label: '支出管理', path: '/finance/expense' }, + { key: 'reimbursement', label: '报销管理', path: '/finance/reimbursement' }, + { key: 'settlement', label: '结算管理', path: '/finance/settlement' }, + { key: 'invoice', label: '发票管理', path: '/finance/invoice' }, + { key: 'finance-accounts', label: '账户管理', path: '/finance/accounts' }, + { key: 'budget', label: '预算管理', path: '/finance/budget' }, + { key: 'finance-reports', label: '财务报表', path: '/finance/reports' }, + { key: 'finance-import', label: '数据导入', path: '/finance/import' }, + { key: 'advanced-reports', label: '高级报表', path: '/finance/advanced-reports' }, + ] + } + ], + routes: [ + { path: 'finance/overview', name: 'FinanceOverview', component: '@/views/finance/overview/index.vue', title: '财务总览' }, + { path: 'finance/income', name: 'Income', component: '@/views/finance/income/index.vue', title: '收入管理' }, + { path: 'finance/expense', name: 'Expense', component: '@/views/finance/expense/index.vue', title: '支出管理' }, + { path: 'finance/reimbursement', name: 'Reimbursement', component: '@/views/finance/reimbursement/index.vue', title: '报销管理' }, + { path: 'finance/settlement', name: 'Settlement', component: '@/views/finance/settlement/index.vue', title: '结算管理' }, + { path: 'finance/invoice', name: 'Invoice', component: '@/views/finance/invoice/index.vue', title: '发票管理' }, + { path: 'finance/accounts', name: 'FinanceAccounts', component: '@/views/finance/accounts/index.vue', title: '账户管理' }, + { path: 'finance/budget', name: 'Budget', component: '@/views/finance/budget/index.vue', title: '预算管理' }, + { path: 'finance/reports', name: 'FinanceReports', component: '@/views/finance/reports/index.vue', title: '财务报表' }, + { path: 'finance/import', name: 'FinanceImport', component: '@/views/finance/import/index.vue', title: '数据导入' }, + { path: 'finance/advanced-reports', name: 'AdvancedReports', component: '@/views/finance/advanced-reports/index.vue', title: '高级报表' }, + ] +} + +// 平台管理模块 +export const platformModule: ModuleConfig = { + id: 'platform', + name: '平台管理', + type: 'framework', + icon: AppstoreOutlined, + menus: [ + { + key: 'platform', + label: '平台管理', + children: [ + { key: 'platform-projects', label: '项目管理', path: '/platform/projects' }, + { key: 'platform-menus', label: '菜单管理', path: '/platform/menus' }, + { key: 'platform-certificates', label: '证书管理', path: '/platform/certificates' } + ] + } + ], + routes: [ + { path: 'platform/projects', name: 'PlatformProjects', component: '@/views/platform/projects/index.vue', title: '项目管理' }, + { path: 'platform/menus', name: 'PlatformMenus', component: '@/views/platform/menus/index.vue', title: '菜单管理' }, + { path: 'platform/certificates', name: 'PlatformCertificates', component: '@/views/platform/certificates/index.vue', title: '证书管理' } + ] +} + +// 所有框架模块 +export const frameworkModules: ModuleConfig[] = [ + financeModule, + platformModule, + systemModule +] + +// ==================== 项目配置 ==================== + +export interface ProjectConfig { + id: ProjectId + name: string + logo: string + shortName: string + description: string + modules: ModuleConfig[] +} + +// 框架项目配置 +export const frameworkProject: ProjectConfig = { + id: 'framework', + name: '管理后台框架', + logo: '管', + shortName: '管理', + description: '通用管理后台框架', + modules: frameworkModules +} + +// 所有项目配置 +export const projectConfigs: Record = { + framework: frameworkProject, + default: frameworkProject +} + +// ==================== 工具函数 ==================== + +/** + * 获取当前项目配置 + */ +export function getCurrentProject(): ProjectConfig { + return projectConfigs[CURRENT_PROJECT] || projectConfigs.default +} + +/** + * 获取当前项目的所有模块 + */ +export function getAllModules(): ModuleConfig[] { + return frameworkModules +} + +/** + * 获取当前项目的菜单配置 + */ +export function getMenuConfig(): MenuItem[] { + const modules = getAllModules() + const menus: MenuItem[] = [] + + modules.forEach(module => { + module.menus.forEach(menu => { + if (!menu.children) { + menus.push({ + ...menu, + icon: module.icon + }) + } else { + menus.push({ + ...menu, + icon: module.icon + }) + } + }) + }) + + return menus +} + +/** + * 获取菜单项到路由的映射 + */ +export function getMenuRouteMap(): Record { + const modules = getAllModules() + const map: Record = {} + + modules.forEach(module => { + module.menus.forEach(menu => { + if (menu.path) { + map[menu.key] = menu.path + } + if (menu.children) { + menu.children.forEach(child => { + if (child.path) { + map[child.key] = child.path + } + }) + } + }) + }) + + return map +} + +/** + * 获取所有路由配置 + */ +export function getRouteConfigs(): RouteConfig[] { + const modules = getAllModules() + const routes: RouteConfig[] = [] + + modules.forEach(module => { + routes.push(...module.routes) + }) + + return routes +} + +/** + * 获取业务模块菜单(框架版无业务模块) + */ +export function getBusinessMenus(): MenuItem[] { + return [] +} + +/** + * 获取框架模块菜单 + */ +export function getFrameworkMenus(): MenuItem[] { + return getMenuConfig() +} diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue new file mode 100644 index 0000000..1b537e7 --- /dev/null +++ b/src/layouts/MainLayout.vue @@ -0,0 +1,544 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..3716f87 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,15 @@ +import { createApp } from 'vue' +import App from './App.vue' +import router from './router' +import pinia from './stores' + +// Ant Design Vue 全局样式 +import 'ant-design-vue/dist/reset.css' +import './style.css' + +const app = createApp(App) + +app.use(pinia) +app.use(router) + +app.mount('#app') diff --git a/src/mock/approval.ts b/src/mock/approval.ts new file mode 100644 index 0000000..868c6ed --- /dev/null +++ b/src/mock/approval.ts @@ -0,0 +1,458 @@ +/** + * 审批流程管理模拟数据 + */ +import type { + ApprovalTemplate, + ApprovalNode, + ApprovalInstance, + ApprovalScenario, + ApprovalInstanceStatus, + ApproverInfo, + ApprovalTemplateQueryParams, + ApprovalTemplateListResult, + ApprovalInstanceQueryParams, + ApprovalInstanceListResult, + ApprovalStats, + NodeApprovalRecord +} from '@/types/approval' + +// 模拟审批人 +const mockApprovers: ApproverInfo[] = [ + { id: 1, name: '张经理', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=mgr1', role: '部门经理', department: '技术部' }, + { id: 2, name: '李总监', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=dir1', role: '技术总监', department: '技术部' }, + { id: 3, name: '王财务', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=fin1', role: '财务主管', department: '财务部' }, + { id: 4, name: '赵HR', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=hr1', role: 'HR经理', department: '人事部' }, + { id: 5, name: '孙法务', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=legal1', role: '法务专员', department: '法务部' }, + { id: 6, name: '周运营', avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=op1', role: '运营主管', department: '运营部' } +] + +// 生成审批节点 +function createNodes(templateType: string): ApprovalNode[] { + const nodeTemplates: Record = { + project_publish: [ + { id: 1, name: '内容初审', approverType: 'role', approverRole: '运营专员', approvalMode: 'or', order: 1 }, + { id: 2, name: '运营主管审核', approverType: 'specified', approverIds: [6], approvalMode: 'and', order: 2 }, + { id: 3, name: '技术总监确认', approverType: 'specified', approverIds: [2], approvalMode: 'and', order: 3, timeoutHours: 24, timeoutAction: 'skip' } + ], + withdrawal: [ + { id: 1, name: '财务初审', approverType: 'role', approverRole: '财务专员', approvalMode: 'or', order: 1 }, + { id: 2, name: '财务主管审批', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 2 }, + { id: 3, name: '总经理审批', approverType: 'superior', approvalMode: 'and', order: 3, timeoutHours: 48, timeoutAction: 'reject' } + ], + contract: [ + { id: 1, name: '法务审核', approverType: 'specified', approverIds: [5], approvalMode: 'and', order: 1 }, + { id: 2, name: '部门负责人审批', approverType: 'superior', approvalMode: 'and', order: 2 }, + { id: 3, name: '总监审批', approverType: 'specified', approverIds: [2], approvalMode: 'and', order: 3 } + ], + certification: [ + { id: 1, name: 'HR审核', approverType: 'specified', approverIds: [4], approvalMode: 'and', order: 1 }, + { id: 2, name: '运营确认', approverType: 'specified', approverIds: [6], approvalMode: 'and', order: 2 } + ], + content: [ + { id: 1, name: '内容审核', approverType: 'role', approverRole: '内容审核员', approvalMode: 'or', order: 1 }, + { id: 2, name: '运营复核', approverType: 'specified', approverIds: [6], approvalMode: 'and', order: 2, timeoutHours: 12, timeoutAction: 'skip' } + ], + // ==================== 财务管理审批流程 ==================== + // 费用报销审批流程 + expense_reimbursement: [ + { id: 1, name: '直属领导审批', approverType: 'superior', approvalMode: 'and', order: 1, timeoutHours: 24 }, + { id: 2, name: '财务审核', approverType: 'role', approverRole: '财务专员', approvalMode: 'or', order: 2, timeoutHours: 48 }, + { id: 3, name: '财务主管审批', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 3, timeoutHours: 24, timeoutAction: 'skip' } + ], + // 大额费用报销(超过5000元) + expense_reimbursement_large: [ + { id: 1, name: '直属领导审批', approverType: 'superior', approvalMode: 'and', order: 1, timeoutHours: 24 }, + { id: 2, name: '部门总监审批', approverType: 'specified', approverIds: [2], approvalMode: 'and', order: 2, timeoutHours: 24 }, + { id: 3, name: '财务审核', approverType: 'role', approverRole: '财务专员', approvalMode: 'or', order: 3, timeoutHours: 48 }, + { id: 4, name: '财务总监审批', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 4, timeoutHours: 24 }, + { id: 5, name: 'CEO最终审批', approverType: 'specified', approverIds: [1], approvalMode: 'and', order: 5, timeoutHours: 48, timeoutAction: 'reject' } + ], + // 付款申请审批流程 + payment_request: [ + { id: 1, name: '申请人确认', approverType: 'self_select', approvalMode: 'and', order: 1 }, + { id: 2, name: '部门负责人审批', approverType: 'superior', approvalMode: 'and', order: 2, timeoutHours: 24 }, + { id: 3, name: '财务核对', approverType: 'role', approverRole: '财务专员', approvalMode: 'or', order: 3, timeoutHours: 24 }, + { id: 4, name: '财务主管审批', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 4, timeoutHours: 24 }, + { id: 5, name: '出纳付款', approverType: 'role', approverRole: '出纳', approvalMode: 'or', order: 5 } + ], + // 采购申请审批流程 + purchase_request: [ + { id: 1, name: '部门负责人审批', approverType: 'superior', approvalMode: 'and', order: 1, timeoutHours: 24 }, + { id: 2, name: '采购部审核', approverType: 'role', approverRole: '采购专员', approvalMode: 'or', order: 2, timeoutHours: 48 }, + { id: 3, name: '财务预算审核', approverType: 'role', approverRole: '财务专员', approvalMode: 'or', order: 3, timeoutHours: 24 }, + { id: 4, name: '总经理审批', approverType: 'specified', approverIds: [1], approvalMode: 'and', order: 4, timeoutHours: 48, timeoutAction: 'reject' } + ], + // 预算调整审批流程 + budget_adjustment: [ + { id: 1, name: '部门负责人审批', approverType: 'superior', approvalMode: 'and', order: 1, timeoutHours: 24 }, + { id: 2, name: '财务主管审核', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 2, timeoutHours: 48 }, + { id: 3, name: 'CFO审批', approverType: 'superior', approvalMode: 'and', order: 3, timeoutHours: 48 }, + { id: 4, name: 'CEO最终审批', approverType: 'specified', approverIds: [1], approvalMode: 'and', order: 4, timeoutHours: 72, timeoutAction: 'reject' } + ], + // 发票申请审批流程 + invoice_apply: [ + { id: 1, name: '销售确认', approverType: 'specified', approverIds: [6], approvalMode: 'and', order: 1, timeoutHours: 12 }, + { id: 2, name: '财务开票', approverType: 'role', approverRole: '财务专员', approvalMode: 'or', order: 2, timeoutHours: 24 }, + { id: 3, name: '财务主管复核', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 3, timeoutHours: 12, timeoutAction: 'skip' } + ] + } + return nodeTemplates[templateType] || [] +} + +// 生成审批模板 +function generateTemplates(): ApprovalTemplate[] { + const templates: ApprovalTemplate[] = [ + { + id: 1, + name: '项目发布审批流程', + description: '项目发布前的内容审核和技术确认流程', + scenario: 'project_publish', + nodes: createNodes('project_publish'), + enabled: true, + createdAt: '2024-01-10T10:00:00Z', + updatedAt: '2024-06-20T15:30:00Z' + }, + { + id: 2, + name: '提现申请审批流程', + description: '用户提现申请的财务审批流程', + scenario: 'withdrawal', + nodes: createNodes('withdrawal'), + enabled: true, + createdAt: '2024-01-15T09:00:00Z', + updatedAt: '2024-05-10T11:20:00Z' + }, + { + id: 3, + name: '合同签署审批流程', + description: '合同签署前的法务和管理层审批', + scenario: 'contract', + nodes: createNodes('contract'), + enabled: true, + createdAt: '2024-02-01T14:00:00Z', + updatedAt: '2024-07-05T16:45:00Z' + }, + { + id: 4, + name: '用户认证审批流程', + description: '用户实名认证和企业认证的人工审核', + scenario: 'certification', + nodes: createNodes('certification'), + enabled: true, + createdAt: '2024-02-20T11:00:00Z', + updatedAt: '2024-04-18T09:15:00Z' + }, + { + id: 5, + name: '内容审核流程', + description: '帖子和文章发布前的内容安全审核', + scenario: 'content', + nodes: createNodes('content'), + enabled: true, + createdAt: '2024-03-01T08:30:00Z', + updatedAt: '2024-08-12T14:00:00Z' + }, + { + id: 6, + name: '大额提现审批流程', + description: '超过10万元的提现需要多级审批', + scenario: 'withdrawal', + nodes: [ + { id: 1, name: '财务初审', approverType: 'role', approverRole: '财务专员', approvalMode: 'and', order: 1 }, + { id: 2, name: '财务主管审批', approverType: 'specified', approverIds: [3], approvalMode: 'and', order: 2 }, + { id: 3, name: 'CFO审批', approverType: 'superior', approvalMode: 'and', order: 3 }, + { id: 4, name: 'CEO最终确认', approverType: 'specified', approverIds: [1], approvalMode: 'and', order: 4 } + ], + enabled: false, + createdAt: '2024-04-10T10:00:00Z', + updatedAt: '2024-04-10T10:00:00Z' + }, + // ==================== 财务管理审批模板 ==================== + { + id: 7, + name: '费用报销审批流程', + description: '日常费用报销审批,适用于5000元以下的报销申请。流程:直属领导→财务审核→财务主管', + scenario: 'expense_reimbursement', + nodes: createNodes('expense_reimbursement'), + enabled: true, + createdAt: '2024-05-01T09:00:00Z', + updatedAt: '2024-09-15T10:30:00Z' + }, + { + id: 8, + name: '大额费用报销审批流程', + description: '超过5000元的费用报销需要多级审批。流程:直属领导→部门总监→财务审核→财务总监→CEO', + scenario: 'expense_reimbursement', + nodes: createNodes('expense_reimbursement_large'), + enabled: true, + createdAt: '2024-05-05T10:00:00Z', + updatedAt: '2024-09-20T14:00:00Z' + }, + { + id: 9, + name: '付款申请审批流程', + description: '供应商付款、服务费支付等对外付款申请审批。流程:申请人确认→部门负责人→财务核对→财务主管→出纳付款', + scenario: 'payment_request', + nodes: createNodes('payment_request'), + enabled: true, + createdAt: '2024-05-10T11:00:00Z', + updatedAt: '2024-10-01T09:00:00Z' + }, + { + id: 10, + name: '采购申请审批流程', + description: '物资采购、服务采购申请审批。流程:部门负责人→采购部审核→财务预算审核→总经理审批', + scenario: 'purchase_request', + nodes: createNodes('purchase_request'), + enabled: true, + createdAt: '2024-06-01T09:00:00Z', + updatedAt: '2024-10-10T16:00:00Z' + }, + { + id: 11, + name: '预算调整审批流程', + description: '年度预算调整、部门预算追加申请。流程:部门负责人→财务主管→CFO→CEO,需要多级管理层批准', + scenario: 'budget_adjustment', + nodes: createNodes('budget_adjustment'), + enabled: true, + createdAt: '2024-06-15T14:00:00Z', + updatedAt: '2024-11-01T10:00:00Z' + }, + { + id: 12, + name: '发票申请审批流程', + description: '客户发票开具申请审批。流程:销售确认→财务开票→财务主管复核', + scenario: 'invoice_apply', + nodes: createNodes('invoice_apply'), + enabled: true, + createdAt: '2024-07-01T09:00:00Z', + updatedAt: '2024-11-15T11:00:00Z' + } + ] + return templates +} + +// 生成审批实例 +function generateInstances(): ApprovalInstance[] { + const instances: ApprovalInstance[] = [] + const statuses: ApprovalInstanceStatus[] = ['pending', 'in_progress', 'approved', 'rejected', 'withdrawn'] + const businessTypes = [ + { type: 'project', title: '企业官网开发项目', scenario: 'project_publish' as ApprovalScenario }, + { type: 'withdrawal', title: '提现申请 - ¥15,000', scenario: 'withdrawal' as ApprovalScenario }, + { type: 'contract', title: 'CRM系统开发服务合同', scenario: 'contract' as ApprovalScenario }, + { type: 'certification', title: '企业认证 - 科技有限公司', scenario: 'certification' as ApprovalScenario }, + { type: 'post', title: '技术分享文章审核', scenario: 'content' as ApprovalScenario }, + // 财务管理审批实例 + { type: 'reimbursement', title: '差旅费报销 - ¥3,280', scenario: 'expense_reimbursement' as ApprovalScenario }, + { type: 'reimbursement', title: '办公用品采购报销 - ¥1,560', scenario: 'expense_reimbursement' as ApprovalScenario }, + { type: 'reimbursement', title: '客户招待费报销 - ¥8,500', scenario: 'expense_reimbursement' as ApprovalScenario }, + { type: 'payment', title: '供应商付款 - 服务器托管费', scenario: 'payment_request' as ApprovalScenario }, + { type: 'payment', title: '房租付款申请 - 12月份', scenario: 'payment_request' as ApprovalScenario }, + { type: 'purchase', title: '办公电脑采购 - 5台', scenario: 'purchase_request' as ApprovalScenario }, + { type: 'purchase', title: '云服务年度续费申请', scenario: 'purchase_request' as ApprovalScenario }, + { type: 'budget', title: '研发部Q4预算追加申请', scenario: 'budget_adjustment' as ApprovalScenario }, + { type: 'invoice', title: '开具增值税专用发票 - ¥50,000', scenario: 'invoice_apply' as ApprovalScenario } + ] + + for (let i = 1; i <= 35; i++) { + const business = businessTypes[i % businessTypes.length]! + const status = statuses[i % statuses.length]! + const submittedAt = new Date(Date.now() - (i * 2 + Math.random() * 5) * 24 * 60 * 60 * 1000) + + const records: NodeApprovalRecord[] = [] + if (status !== 'pending') { + records.push({ + nodeId: 1, + nodeName: '初审', + approverId: mockApprovers[i % mockApprovers.length]!.id, + approverName: mockApprovers[i % mockApprovers.length]!.name, + approverAvatar: mockApprovers[i % mockApprovers.length]!.avatar, + action: 'approve', + comment: '已审核,符合规范', + operatedAt: new Date(submittedAt.getTime() + 2 * 60 * 60 * 1000).toISOString() + }) + } + if (['approved', 'rejected'].includes(status)) { + records.push({ + nodeId: 2, + nodeName: '复审', + approverId: mockApprovers[(i + 1) % mockApprovers.length]!.id, + approverName: mockApprovers[(i + 1) % mockApprovers.length]!.name, + approverAvatar: mockApprovers[(i + 1) % mockApprovers.length]!.avatar, + action: status === 'approved' ? 'approve' : 'reject', + comment: status === 'approved' ? '审批通过' : '不符合要求,请修改后重新提交', + operatedAt: new Date(submittedAt.getTime() + 8 * 60 * 60 * 1000).toISOString() + }) + } + + instances.push({ + id: i, + templateId: (i % 5) + 1, + templateName: ['项目发布审批', '提现申请审批', '合同签署审批', '用户认证审批', '内容审核'][i % 5]!, + scenario: business.scenario, + businessType: business.type, + businessId: 1000 + i, + businessTitle: business.title, + initiatorId: 100 + i, + initiatorName: ['张三', '李四', '王五', '赵六', '孙七'][i % 5]!, + initiatorAvatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=user${i}`, + status, + currentNodeId: status === 'in_progress' ? 2 : undefined, + currentNodeName: status === 'in_progress' ? '复审' : undefined, + records, + submittedAt: submittedAt.toISOString(), + completedAt: ['approved', 'rejected'].includes(status) + ? new Date(submittedAt.getTime() + 24 * 60 * 60 * 1000).toISOString() + : undefined, + createdAt: submittedAt.toISOString() + }) + } + + return instances.sort((a, b) => new Date(b.submittedAt).getTime() - new Date(a.submittedAt).getTime()) +} + +let mockTemplates = generateTemplates() +let mockInstances = generateInstances() + +function delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +// ================== 模板 API ================== + +export async function mockGetApprovalStats(): Promise { + await delay(100) + return { + totalTemplates: mockTemplates.length, + enabledTemplates: mockTemplates.filter(t => t.enabled).length, + totalInstances: mockInstances.length, + pendingInstances: mockInstances.filter(i => i.status === 'pending').length, + inProgressInstances: mockInstances.filter(i => i.status === 'in_progress').length, + approvedInstances: mockInstances.filter(i => i.status === 'approved').length, + rejectedInstances: mockInstances.filter(i => i.status === 'rejected').length + } +} + +export async function mockGetApprovalTemplateList( + params: ApprovalTemplateQueryParams = {} +): Promise { + await delay(200) + const { page = 1, pageSize = 10, keyword, scenario, enabled } = params + + let filtered = [...mockTemplates] + + if (keyword) { + const kw = keyword.toLowerCase() + filtered = filtered.filter(t => + t.name.toLowerCase().includes(kw) || + t.description?.toLowerCase().includes(kw) + ) + } + if (scenario) { + filtered = filtered.filter(t => t.scenario === scenario) + } + if (enabled !== undefined) { + filtered = filtered.filter(t => t.enabled === enabled) + } + + const start = (page - 1) * pageSize + return { + list: filtered.slice(start, start + pageSize), + total: filtered.length, + page, + pageSize + } +} + +export async function mockGetApprovalTemplateById(id: number): Promise { + await delay(100) + return mockTemplates.find(t => t.id === id) || null +} + +export async function mockCreateApprovalTemplate(template: Omit): Promise { + await delay(200) + const newTemplate: ApprovalTemplate = { + ...template, + id: Math.max(...mockTemplates.map(t => t.id)) + 1, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + mockTemplates.push(newTemplate) + return newTemplate +} + +export async function mockUpdateApprovalTemplate(id: number, data: Partial): Promise { + await delay(200) + const index = mockTemplates.findIndex(t => t.id === id) + if (index !== -1) { + mockTemplates[index] = { ...mockTemplates[index]!, ...data, updatedAt: new Date().toISOString() } + } +} + +export async function mockDeleteApprovalTemplate(id: number): Promise { + await delay(200) + mockTemplates = mockTemplates.filter(t => t.id !== id) +} + +export async function mockToggleApprovalTemplate(id: number): Promise { + await delay(100) + const template = mockTemplates.find(t => t.id === id) + if (template) { + template.enabled = !template.enabled + template.updatedAt = new Date().toISOString() + } +} + +// ================== 实例 API ================== + +export async function mockGetApprovalInstanceList( + params: ApprovalInstanceQueryParams = {} +): Promise { + await delay(200) + const { page = 1, pageSize = 10, keyword, scenario, status, startDate, endDate } = params + + let filtered = [...mockInstances] + + if (keyword) { + const kw = keyword.toLowerCase() + filtered = filtered.filter(i => + i.businessTitle.toLowerCase().includes(kw) || + i.initiatorName.toLowerCase().includes(kw) || + i.templateName.toLowerCase().includes(kw) + ) + } + if (scenario) { + filtered = filtered.filter(i => i.scenario === scenario) + } + if (status) { + filtered = filtered.filter(i => i.status === status) + } + if (startDate) { + filtered = filtered.filter(i => new Date(i.submittedAt) >= new Date(startDate)) + } + if (endDate) { + const end = new Date(endDate) + end.setHours(23, 59, 59, 999) + filtered = filtered.filter(i => new Date(i.submittedAt) <= end) + } + + const start = (page - 1) * pageSize + return { + list: filtered.slice(start, start + pageSize), + total: filtered.length, + page, + pageSize + } +} + +export async function mockGetApprovalInstanceById(id: number): Promise { + await delay(100) + return mockInstances.find(i => i.id === id) || null +} + +// ================== 审批人 API ================== + +export async function mockGetApprovers(): Promise { + await delay(100) + return mockApprovers +} diff --git a/src/mock/finance.ts b/src/mock/finance.ts new file mode 100644 index 0000000..1e1fd2f --- /dev/null +++ b/src/mock/finance.ts @@ -0,0 +1,1071 @@ +/** + * 财务模块 Mock 数据 + */ +import type { + SettlementItem, InvoiceItem, SettlementQueryParams, + FinanceAccount, IncomeRecord, IncomePayment, ExpenseRecord, + ReimbursementRecord, BudgetRecord, FinanceOverview, CategoryStats, + IncomeQueryParams, ExpenseQueryParams, ReimbursementQueryParams, BudgetQueryParams, + IncomeType, IncomeStatus, ExpenseType, ExpenseStatus, + ReimbursementType, ReimbursementStatus, BudgetPeriod +} from '@/types' + +// 模拟人才数据(财务模块独立使用) +const talents = [ + { id: 1, realName: '张伟', skills: ['Vue', 'React'] }, + { id: 2, realName: '李明', skills: ['Java', 'Spring'] }, + { id: 3, realName: '王芳', skills: ['Python', 'Django'] }, + { id: 4, realName: '刘强', skills: ['Node.js', 'Express'] }, + { id: 5, realName: '陈静', skills: ['Go', 'Kubernetes'] } +] + +// ==================== 原有结算发票数据 ==================== +const mockSettlements: SettlementItem[] = [] +const mockInvoices: InvoiceItem[] = [] + +function initSettlementData() { + const count = 35 + const baseDate = new Date('2024-01-01').getTime() + + for (let i = 0; i < count; i++) { + const talent = talents[i % talents.length]! + const projects = ['企业官网重构', '电商小程序', 'SaaS后台管理', '数据大屏可视化', 'AI模型训练平台'] + const projectName = projects[i % projects.length]! + + const totalAmount = Math.floor(Math.random() * 450 + 50) * 100 + const platformFeeRate = 0.1 + const platformFee = totalAmount * platformFeeRate + const taxableAmount = totalAmount - platformFee + const taxRate = 0.06 + const taxAmount = Math.floor(taxableAmount * taxRate) + const actualAmount = taxableAmount - taxAmount + + const statuses = ['pending', 'paying', 'completed', 'completed', 'completed'] + const status = statuses[i % statuses.length] as SettlementItem['status'] + + const createdAt = new Date(baseDate + Math.random() * 150 * 24 * 60 * 60 * 1000).toISOString() + + const item: SettlementItem = { + id: 10000 + i, + projectId: 200 + i, + projectName, + talentId: talent.id, + talentName: talent.realName, + period: `2024-${String(Math.floor(Math.random() * 6) + 1).padStart(2, '0')}`, + totalAmount, + platformFee, + taxableAmount, + taxRate, + taxAmount, + actualAmount, + status, + invoiceStatus: status === 'completed' ? (Math.random() > 0.2 ? 'received' : 'none') : 'pending', + bankInfo: { + accountName: talent.realName, + accountNo: `622202${String(Math.floor(Math.random() * 1000000000)).padStart(10, '0')}`, + bankName: ['招商银行', '建设银行', '工商银行'][i % 3]! + }, + createdAt, + auditTime: status !== 'pending' ? new Date(new Date(createdAt).getTime() + 86400000).toISOString() : undefined, + paymentTime: status === 'completed' ? new Date(new Date(createdAt).getTime() + 172800000).toISOString() : undefined + } + + mockSettlements.push(item) + + if (item.invoiceStatus === 'received') { + mockInvoices.push({ + id: 5000 + i, + settlementId: item.id, + type: Math.random() > 0.5 ? 'personal' : 'vat_normal', + title: Math.random() > 0.5 ? talent.realName : `${talent.realName}的工作室`, + amount: item.taxableAmount, + status: Math.random() > 0.3 ? 'issued' : 'pending', + submitTime: new Date(new Date(createdAt).getTime() + 200000000).toISOString(), + issueTime: Math.random() > 0.3 ? new Date(new Date(createdAt).getTime() + 250000000).toISOString() : undefined + }) + } + } + + mockSettlements.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) +} + +initSettlementData() + +// ==================== 账户数据 ==================== +const mockAccounts: FinanceAccount[] = [ + { + id: 1, + name: '公司对公账户', + type: 'corporate', + bankName: '中国工商银行', + bankBranch: '杭州市西湖支行', + accountNo: '1202023019300041234', + balance: 2568000.00, + status: 'active', + isDefault: true, + createdAt: '2023-01-01T00:00:00.000Z', + updatedAt: '2024-12-01T00:00:00.000Z' + }, + { + id: 2, + name: '微信商户号-主', + type: 'merchant', + merchantId: '1234567890', + merchantPlatform: 'wechat', + appId: 'wx1234567890abcdef', + balance: 158600.50, + status: 'active', + isDefault: false, + remark: '主要收款账户', + createdAt: '2023-03-15T00:00:00.000Z', + updatedAt: '2024-12-01T00:00:00.000Z' + }, + { + id: 3, + name: '微信商户号-小程序', + type: 'merchant', + merchantId: '1234567891', + merchantPlatform: 'wechat', + appId: 'wx1234567890abcde1', + balance: 45200.00, + status: 'active', + isDefault: false, + remark: '小程序专用', + createdAt: '2023-06-01T00:00:00.000Z', + updatedAt: '2024-12-01T00:00:00.000Z' + }, + { + id: 4, + name: '支付宝商户号', + type: 'merchant', + merchantId: '2088123456789012', + merchantPlatform: 'alipay', + balance: 89500.00, + status: 'active', + isDefault: false, + createdAt: '2023-05-01T00:00:00.000Z', + updatedAt: '2024-12-01T00:00:00.000Z' + }, + { + id: 5, + name: '银联商户号', + type: 'merchant', + merchantId: '898340159123456', + merchantPlatform: 'unionpay', + balance: 23800.00, + status: 'active', + isDefault: false, + createdAt: '2023-08-01T00:00:00.000Z', + updatedAt: '2024-12-01T00:00:00.000Z' + }, + { + id: 6, + name: '其他收款渠道', + type: 'merchant', + merchantId: 'OTHER001', + merchantPlatform: 'other', + balance: 12000.00, + status: 'active', + isDefault: false, + remark: '线下收款统计', + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-12-01T00:00:00.000Z' + } +] + +// ==================== 收入数据 ==================== +const mockIncomes: IncomeRecord[] = [] +const mockIncomePayments: IncomePayment[] = [] + +function initIncomeData() { + const customers = [ + { id: 1, name: '杭州科技有限公司', contact: '张经理' }, + { id: 2, name: '上海网络科技', contact: '李总' }, + { id: 3, name: '深圳创新科技', contact: '王经理' }, + { id: 4, name: '北京互联网公司', contact: '赵总' }, + { id: 5, name: '广州数据科技', contact: '刘经理' } + ] + + const incomeTypes: IncomeType[] = ['project', 'service_fee', 'consulting', 'commission', 'other'] + const statuses: IncomeStatus[] = ['pending', 'partial', 'received', 'overdue'] + + for (let i = 0; i < 50; i++) { + const customer = customers[i % customers.length]! + const type = incomeTypes[i % incomeTypes.length]! + const status = statuses[Math.floor(Math.random() * 4)]! + const account = mockAccounts[Math.floor(Math.random() * mockAccounts.length)]! + + const totalAmount = Math.floor(Math.random() * 100000 + 5000) + let receivedAmount = 0 + if (status === 'received') { + receivedAmount = totalAmount + } else if (status === 'partial') { + receivedAmount = Math.floor(totalAmount * (0.3 + Math.random() * 0.5)) + } + + const createdAt = new Date(Date.now() - Math.random() * 180 * 24 * 60 * 60 * 1000).toISOString() + + const income: IncomeRecord = { + id: 20000 + i, + incomeNo: `IN${new Date(createdAt).getFullYear()}${String(i + 1).padStart(6, '0')}`, + type, + title: type === 'project' ? `${customer.name}项目开发款` : + type === 'service_fee' ? '平台服务费收入' : + type === 'consulting' ? '技术咨询服务' : + type === 'commission' ? '项目佣金' : '其他收入', + customerId: customer.id, + customerName: customer.name, + customerContact: customer.contact, + projectId: type === 'project' ? 100 + i : undefined, + projectName: type === 'project' ? `${customer.name.slice(0, 4)}项目` : undefined, + contractNo: Math.random() > 0.5 ? `CT${new Date(createdAt).getFullYear()}${String(i + 1).padStart(4, '0')}` : undefined, + totalAmount, + receivedAmount, + pendingAmount: totalAmount - receivedAmount, + accountId: account.id, + accountName: account.name, + status, + expectedDate: new Date(new Date(createdAt).getTime() + 30 * 24 * 60 * 60 * 1000).toISOString(), + actualDate: status === 'received' ? new Date(new Date(createdAt).getTime() + Math.random() * 20 * 24 * 60 * 60 * 1000).toISOString() : undefined, + invoiceRequired: Math.random() > 0.3, + invoiceIssued: status === 'received' && Math.random() > 0.4, + createdBy: '管理员', + createdAt, + updatedAt: createdAt + } + + mockIncomes.push(income) + + // 生成收款记录 + if (receivedAmount > 0) { + mockIncomePayments.push({ + id: 30000 + i, + incomeId: income.id, + amount: receivedAmount, + paymentMethod: ['bank', 'wechat', 'alipay'][Math.floor(Math.random() * 3)] as any, + accountId: account.id, + transactionNo: `TXN${Date.now()}${i}`, + paymentDate: income.actualDate || createdAt, + createdBy: '财务', + createdAt + }) + } + } + + mockIncomes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) +} + +initIncomeData() + +// ==================== 支出数据 ==================== +const mockExpenses: ExpenseRecord[] = [] + +function initExpenseData() { + const expenseTypes: ExpenseType[] = ['salary', 'office', 'rent', 'travel', 'marketing', 'equipment', 'service', 'tax', 'social_insurance', 'other'] + const statuses: ExpenseStatus[] = ['draft', 'pending', 'approved', 'paid', 'rejected'] + const departments = [ + { id: 1, name: '研发部' }, + { id: 2, name: '市场部' }, + { id: 3, name: '运营部' }, + { id: 4, name: '行政部' }, + { id: 5, name: '财务部' } + ] + + const payees = [ + { name: '房东张某', account: '6222021234567890123', bank: '建设银行' }, + { name: '中国电信', account: '6225881234567890', bank: '工商银行' }, + { name: '办公用品供应商', account: '6212261234567890', bank: '农业银行' }, + { name: '广告公司', account: '6217001234567890', bank: '招商银行' }, + { name: 'IT设备供应商', account: '6228481234567890', bank: '中国银行' } + ] + + for (let i = 0; i < 60; i++) { + const type = expenseTypes[i % expenseTypes.length]! + const status = statuses[Math.floor(Math.random() * 5)]! + const department = departments[i % departments.length]! + const payee = payees[i % payees.length]! + const account = mockAccounts[0]! // 对公账户 + + const amount = type === 'salary' ? Math.floor(Math.random() * 200000 + 50000) : + type === 'rent' ? Math.floor(Math.random() * 50000 + 10000) : + Math.floor(Math.random() * 20000 + 500) + + const createdAt = new Date(Date.now() - Math.random() * 180 * 24 * 60 * 60 * 1000).toISOString() + + const expense: ExpenseRecord = { + id: 40000 + i, + expenseNo: `EX${new Date(createdAt).getFullYear()}${String(i + 1).padStart(6, '0')}`, + type, + title: type === 'salary' ? `${new Date(createdAt).getMonth() + 1}月工资发放` : + type === 'rent' ? `${new Date(createdAt).getMonth() + 1}月房租水电` : + type === 'office' ? '办公用品采购' : + type === 'travel' ? '出差费用' : + type === 'marketing' ? '市场推广费用' : + type === 'equipment' ? '设备采购' : + type === 'service' ? '外包服务费' : + type === 'tax' ? '税费缴纳' : + type === 'social_insurance' ? '社保公积金' : '其他支出', + payeeName: payee.name, + payeeAccount: payee.account, + payeeBankName: payee.bank, + amount, + departmentId: department.id, + departmentName: department.name, + accountId: status === 'paid' ? account.id : undefined, + accountName: status === 'paid' ? account.name : undefined, + status, + approvalId: ['pending', 'approved', 'paid', 'rejected'].includes(status) ? 1000 + i : undefined, + attachments: Math.random() > 0.5 ? ['https://example.com/receipt1.jpg'] : undefined, + paymentDate: status === 'paid' ? new Date(new Date(createdAt).getTime() + 5 * 24 * 60 * 60 * 1000).toISOString() : undefined, + remark: Math.random() > 0.7 ? '备注信息' : undefined, + createdBy: '财务管理员', + createdAt, + updatedAt: createdAt + } + + mockExpenses.push(expense) + } + + mockExpenses.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) +} + +initExpenseData() + +// ==================== 报销数据 ==================== +const mockReimbursements: ReimbursementRecord[] = [] + +function initReimbursementData() { + const types: ReimbursementType[] = ['travel', 'meal', 'transport', 'communication', 'office', 'other'] + const statuses: ReimbursementStatus[] = ['draft', 'pending', 'approved', 'paid', 'rejected'] + const applicants = [ + { id: 1, name: '张三', department: '研发部' }, + { id: 2, name: '李四', department: '市场部' }, + { id: 3, name: '王五', department: '运营部' }, + { id: 4, name: '赵六', department: '行政部' }, + { id: 5, name: '钱七', department: '产品部' } + ] + + for (let i = 0; i < 45; i++) { + const type = types[i % types.length]! + const status = statuses[Math.floor(Math.random() * 5)]! + const applicant = applicants[i % applicants.length]! + + const itemCount = Math.floor(Math.random() * 4) + 1 + const items = Array.from({ length: itemCount }, (_, idx) => ({ + id: i * 10 + idx, + type: types[Math.floor(Math.random() * types.length)]!, + description: ['餐费', '交通费', '住宿费', '通讯费', '办公用品'][Math.floor(Math.random() * 5)]!, + amount: Math.floor(Math.random() * 500 + 50), + occurDate: new Date(Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]!, + attachments: Math.random() > 0.3 ? ['https://example.com/receipt.jpg'] : undefined + })) + + const totalAmount = items.reduce((sum, item) => sum + item.amount, 0) + const createdAt = new Date(Date.now() - Math.random() * 90 * 24 * 60 * 60 * 1000).toISOString() + + const reimbursement: ReimbursementRecord = { + id: 50000 + i, + reimbursementNo: `RB${new Date(createdAt).getFullYear()}${String(i + 1).padStart(6, '0')}`, + type, + title: type === 'travel' ? '出差报销' : + type === 'meal' ? '餐饮报销' : + type === 'transport' ? '交通费报销' : + type === 'communication' ? '通讯费报销' : + type === 'office' ? '办公用品报销' : '其他报销', + applicantId: applicant.id, + applicantName: applicant.name, + departmentName: applicant.department, + totalAmount, + items, + status, + approvalId: ['pending', 'approved', 'paid', 'rejected'].includes(status) ? 2000 + i : undefined, + currentApprover: status === 'pending' ? '部门主管' : undefined, + bankAccountName: applicant.name, + bankAccountNo: `6222${String(Math.floor(Math.random() * 10000000000000000)).slice(0, 16)}`, + bankName: ['工商银行', '建设银行', '招商银行'][i % 3], + paymentDate: status === 'paid' ? new Date(new Date(createdAt).getTime() + 7 * 24 * 60 * 60 * 1000).toISOString() : undefined, + createdAt, + updatedAt: createdAt + } + + mockReimbursements.push(reimbursement) + } + + mockReimbursements.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) +} + +initReimbursementData() + +// ==================== 预算数据 ==================== +const mockBudgets: BudgetRecord[] = [] + +function initBudgetData() { + const departments = [ + { id: 1, name: '研发部' }, + { id: 2, name: '市场部' }, + { id: 3, name: '运营部' }, + { id: 4, name: '行政部' }, + { id: 5, name: '财务部' } + ] + + const expenseTypes: ExpenseType[] = ['salary', 'office', 'travel', 'marketing', 'equipment', 'other'] + + for (let i = 0; i < 20; i++) { + const department = departments[i % departments.length]! + const year = 2024 + const month = (i % 12) + 1 + + const items = expenseTypes.map(type => { + const budgetAmount = type === 'salary' ? 300000 : + type === 'marketing' ? 50000 : + Math.floor(Math.random() * 20000 + 5000) + const usedAmount = Math.floor(budgetAmount * Math.random()) + return { + expenseType: type, + budgetAmount, + usedAmount, + remainingAmount: budgetAmount - usedAmount + } + }) + + const totalBudget = items.reduce((sum, item) => sum + item.budgetAmount, 0) + const usedAmount = items.reduce((sum, item) => sum + item.usedAmount, 0) + + const budget: BudgetRecord = { + id: 60000 + i, + name: `${department.name}${year}年${month}月预算`, + period: 'monthly', + year, + month, + departmentId: department.id, + departmentName: department.name, + items, + totalBudget, + usedAmount, + remainingAmount: totalBudget - usedAmount, + usageRate: Math.round((usedAmount / totalBudget) * 100), + status: month <= new Date().getMonth() + 1 ? 'active' : 'draft', + createdBy: '财务管理员', + createdAt: `${year}-${String(month).padStart(2, '0')}-01T00:00:00.000Z`, + updatedAt: new Date().toISOString() + } + + mockBudgets.push(budget) + } +} + +initBudgetData() + +// ==================== API Methods ==================== + +function delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +// 结算管理 +export async function mockGetSettlements(params: SettlementQueryParams) { + await delay(300) + const { page = 1, pageSize = 10, keyword, status } = params + + let list = [...mockSettlements] + + if (keyword) { + const k = keyword.toLowerCase() + list = list.filter(item => + item.projectName.toLowerCase().includes(k) || + item.talentName.toLowerCase().includes(k) || + String(item.id).includes(k) + ) + } + + if (status) { + list = list.filter(item => item.status === status) + } + + const total = list.length + const start = (page - 1) * pageSize + const resultList = list.slice(start, start + pageSize) + + return { list: resultList, total, page, pageSize } +} + +export async function mockGetSettlementDetail(id: number) { + await delay(150) + return mockSettlements.find(item => item.id === id) || null +} + +export async function mockAuditSettlement(id: number, pass: boolean, remark?: string) { + await delay(300) + const item = mockSettlements.find(i => i.id === id) + if (item) { + if (pass) { + item.status = 'paying' + item.auditTime = new Date().toISOString() + } else { + item.status = 'rejected' + } + if (remark) item.remark = remark + return true + } + return false +} + +export async function mockConfirmPayment(id: number) { + await delay(300) + const item = mockSettlements.find(i => i.id === id) + if (item) { + item.status = 'completed' + item.paymentTime = new Date().toISOString() + return true + } + return false +} + +// 发票管理 +export async function mockGetInvoices() { + await delay(300) + return { list: mockInvoices, total: mockInvoices.length } +} + +export async function mockIssueInvoice(id: number, fileUrl: string) { + await delay(500) + const item = mockInvoices.find(i => i.id === id) + if (item) { + item.status = 'issued' + item.fileUrl = fileUrl + item.issueTime = new Date().toISOString() + return true + } + return false +} + +// 账户管理 +export async function mockGetAccounts() { + await delay(200) + return { list: mockAccounts, total: mockAccounts.length } +} + +export async function mockGetAccountById(id: number) { + await delay(100) + return mockAccounts.find(item => item.id === id) || null +} + +export async function mockSaveAccount(account: Partial) { + await delay(300) + if (account.id) { + const index = mockAccounts.findIndex(i => i.id === account.id) + if (index > -1) { + mockAccounts[index] = { ...mockAccounts[index]!, ...account, updatedAt: new Date().toISOString() } + return mockAccounts[index] + } + } else { + const newAccount: FinanceAccount = { + ...account as FinanceAccount, + id: Math.max(...mockAccounts.map(a => a.id)) + 1, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + mockAccounts.push(newAccount) + return newAccount + } + return null +} + +// 收入管理 +export async function mockGetIncomes(params: IncomeQueryParams) { + await delay(300) + const { page = 1, pageSize = 10, keyword, type, status, accountId, dateRange } = params + + let list = [...mockIncomes] + + if (keyword) { + const k = keyword.toLowerCase() + list = list.filter(item => + item.title.toLowerCase().includes(k) || + item.customerName.toLowerCase().includes(k) || + item.incomeNo.toLowerCase().includes(k) + ) + } + + if (type) list = list.filter(item => item.type === type) + if (status) list = list.filter(item => item.status === status) + if (accountId) list = list.filter(item => item.accountId === accountId) + if (dateRange && dateRange[0] && dateRange[1]) { + const start = new Date(dateRange[0]).getTime() + const end = new Date(dateRange[1]).getTime() + list = list.filter(item => { + const created = new Date(item.createdAt).getTime() + return created >= start && created <= end + }) + } + + const total = list.length + const start = (page - 1) * pageSize + const resultList = list.slice(start, start + pageSize) + + // 计算统计数据 + const stats = { + totalAmount: list.reduce((sum, i) => sum + i.totalAmount, 0), + receivedAmount: list.reduce((sum, i) => sum + i.receivedAmount, 0), + pendingAmount: list.reduce((sum, i) => sum + i.pendingAmount, 0) + } + + return { list: resultList, total, page, pageSize, stats } +} + +export async function mockGetIncomeById(id: number) { + await delay(150) + return mockIncomes.find(item => item.id === id) || null +} + +export async function mockSaveIncome(income: Partial) { + await delay(300) + if (income.id) { + const index = mockIncomes.findIndex(i => i.id === income.id) + if (index > -1) { + mockIncomes[index] = { ...mockIncomes[index]!, ...income, updatedAt: new Date().toISOString() } + return mockIncomes[index] + } + } else { + const newIncome: IncomeRecord = { + ...income as IncomeRecord, + id: Math.max(...mockIncomes.map(i => i.id)) + 1, + incomeNo: `IN${new Date().getFullYear()}${String(mockIncomes.length + 1).padStart(6, '0')}`, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + mockIncomes.unshift(newIncome) + return newIncome + } + return null +} + +export async function mockDeleteIncome(id: number) { + await delay(200) + const index = mockIncomes.findIndex(i => i.id === id) + if (index > -1) { + mockIncomes.splice(index, 1) + return true + } + return false +} + +export async function mockConfirmIncomePayment(id: number, amount: number, paymentMethod: string, accountId: number) { + await delay(300) + const income = mockIncomes.find(i => i.id === id) + if (income) { + income.receivedAmount += amount + income.pendingAmount = income.totalAmount - income.receivedAmount + if (income.pendingAmount <= 0) { + income.status = 'received' + income.actualDate = new Date().toISOString() + } else { + income.status = 'partial' + } + income.updatedAt = new Date().toISOString() + + mockIncomePayments.push({ + id: Math.max(...mockIncomePayments.map(p => p.id), 30000) + 1, + incomeId: id, + amount, + paymentMethod: paymentMethod as any, + accountId, + transactionNo: `TXN${Date.now()}`, + paymentDate: new Date().toISOString(), + createdBy: '财务', + createdAt: new Date().toISOString() + }) + + return true + } + return false +} + +// 支出管理 +export async function mockGetExpenses(params: ExpenseQueryParams) { + await delay(300) + const { page = 1, pageSize = 10, keyword, type, status, dateRange } = params + + let list = [...mockExpenses] + + if (keyword) { + const k = keyword.toLowerCase() + list = list.filter(item => + item.title.toLowerCase().includes(k) || + item.payeeName.toLowerCase().includes(k) || + item.expenseNo.toLowerCase().includes(k) + ) + } + + if (type) list = list.filter(item => item.type === type) + if (status) list = list.filter(item => item.status === status) + if (dateRange && dateRange[0] && dateRange[1]) { + const start = new Date(dateRange[0]).getTime() + const end = new Date(dateRange[1]).getTime() + list = list.filter(item => { + const created = new Date(item.createdAt).getTime() + return created >= start && created <= end + }) + } + + const total = list.length + const startIdx = (page - 1) * pageSize + const resultList = list.slice(startIdx, startIdx + pageSize) + + const stats = { + totalAmount: list.reduce((sum, i) => sum + i.amount, 0), + paidAmount: list.filter(i => i.status === 'paid').reduce((sum, i) => sum + i.amount, 0), + pendingAmount: list.filter(i => ['pending', 'approved'].includes(i.status)).reduce((sum, i) => sum + i.amount, 0) + } + + return { list: resultList, total, page, pageSize, stats } +} + +export async function mockGetExpenseById(id: number) { + await delay(150) + return mockExpenses.find(item => item.id === id) || null +} + +export async function mockSaveExpense(expense: Partial) { + await delay(300) + if (expense.id) { + const index = mockExpenses.findIndex(i => i.id === expense.id) + if (index > -1) { + mockExpenses[index] = { ...mockExpenses[index]!, ...expense, updatedAt: new Date().toISOString() } + return mockExpenses[index] + } + } else { + const newExpense: ExpenseRecord = { + ...expense as ExpenseRecord, + id: Math.max(...mockExpenses.map(i => i.id)) + 1, + expenseNo: `EX${new Date().getFullYear()}${String(mockExpenses.length + 1).padStart(6, '0')}`, + status: 'draft', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + mockExpenses.unshift(newExpense) + return newExpense + } + return null +} + +export async function mockSubmitExpense(id: number) { + await delay(300) + const expense = mockExpenses.find(i => i.id === id) + if (expense && expense.status === 'draft') { + expense.status = 'pending' + expense.updatedAt = new Date().toISOString() + return true + } + return false +} + +export async function mockApproveExpense(id: number, approved: boolean, remark?: string) { + await delay(300) + const expense = mockExpenses.find(i => i.id === id) + if (expense && expense.status === 'pending') { + expense.status = approved ? 'approved' : 'rejected' + if (remark) expense.remark = remark + expense.updatedAt = new Date().toISOString() + return true + } + return false +} + +export async function mockPayExpense(id: number, accountId: number) { + await delay(400) + const expense = mockExpenses.find(i => i.id === id) + if (expense && expense.status === 'approved') { + expense.status = 'paid' + expense.accountId = accountId + expense.accountName = mockAccounts.find(a => a.id === accountId)?.name + expense.paymentDate = new Date().toISOString() + expense.updatedAt = new Date().toISOString() + return true + } + return false +} + +// 报销管理 +export async function mockGetReimbursements(params: ReimbursementQueryParams) { + await delay(300) + const { page = 1, pageSize = 10, keyword, type, status, applicantId, dateRange } = params + + let list = [...mockReimbursements] + + if (keyword) { + const k = keyword.toLowerCase() + list = list.filter(item => + item.title.toLowerCase().includes(k) || + item.applicantName.toLowerCase().includes(k) || + item.reimbursementNo.toLowerCase().includes(k) + ) + } + + if (type) list = list.filter(item => item.type === type) + if (status) list = list.filter(item => item.status === status) + if (applicantId) list = list.filter(item => item.applicantId === applicantId) + if (dateRange && dateRange[0] && dateRange[1]) { + const start = new Date(dateRange[0]).getTime() + const end = new Date(dateRange[1]).getTime() + list = list.filter(item => { + const created = new Date(item.createdAt).getTime() + return created >= start && created <= end + }) + } + + const total = list.length + const startIdx = (page - 1) * pageSize + const resultList = list.slice(startIdx, startIdx + pageSize) + + const stats = { + totalAmount: list.reduce((sum, i) => sum + i.totalAmount, 0), + paidAmount: list.filter(i => i.status === 'paid').reduce((sum, i) => sum + i.totalAmount, 0), + pendingAmount: list.filter(i => ['pending', 'approved'].includes(i.status)).reduce((sum, i) => sum + i.totalAmount, 0) + } + + return { list: resultList, total, page, pageSize, stats } +} + +export async function mockGetReimbursementById(id: number) { + await delay(150) + return mockReimbursements.find(item => item.id === id) || null +} + +export async function mockSaveReimbursement(reimbursement: Partial) { + await delay(300) + if (reimbursement.id) { + const index = mockReimbursements.findIndex(i => i.id === reimbursement.id) + if (index > -1) { + mockReimbursements[index] = { ...mockReimbursements[index]!, ...reimbursement, updatedAt: new Date().toISOString() } + return mockReimbursements[index] + } + } else { + const newReimbursement: ReimbursementRecord = { + ...reimbursement as ReimbursementRecord, + id: Math.max(...mockReimbursements.map(i => i.id)) + 1, + reimbursementNo: `RB${new Date().getFullYear()}${String(mockReimbursements.length + 1).padStart(6, '0')}`, + status: 'draft', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + mockReimbursements.unshift(newReimbursement) + return newReimbursement + } + return null +} + +export async function mockSubmitReimbursement(id: number) { + await delay(300) + const item = mockReimbursements.find(i => i.id === id) + if (item && item.status === 'draft') { + item.status = 'pending' + item.currentApprover = '部门主管' + item.updatedAt = new Date().toISOString() + return true + } + return false +} + +export async function mockApproveReimbursement(id: number, approved: boolean, remark?: string) { + await delay(300) + const item = mockReimbursements.find(i => i.id === id) + if (item && item.status === 'pending') { + item.status = approved ? 'approved' : 'rejected' + item.currentApprover = undefined + if (remark) item.remark = remark + item.updatedAt = new Date().toISOString() + return true + } + return false +} + +export async function mockPayReimbursement(id: number, accountId: number) { + await delay(400) + const item = mockReimbursements.find(i => i.id === id) + if (item && item.status === 'approved') { + item.status = 'paid' + item.paymentAccountId = accountId + item.paymentDate = new Date().toISOString() + item.updatedAt = new Date().toISOString() + return true + } + return false +} + +// 预算管理 +export async function mockGetBudgets(params: BudgetQueryParams) { + await delay(300) + const { page = 1, pageSize = 10, period, year, departmentId, status } = params + + let list = [...mockBudgets] + + if (period) list = list.filter(item => item.period === period) + if (year) list = list.filter(item => item.year === year) + if (departmentId) list = list.filter(item => item.departmentId === departmentId) + if (status) list = list.filter(item => item.status === status) + + const total = list.length + const startIdx = (page - 1) * pageSize + const resultList = list.slice(startIdx, startIdx + pageSize) + + return { list: resultList, total, page, pageSize } +} + +export async function mockGetBudgetById(id: number) { + await delay(150) + return mockBudgets.find(item => item.id === id) || null +} + +export async function mockSaveBudget(budget: Partial) { + await delay(300) + if (budget.id) { + const index = mockBudgets.findIndex(i => i.id === budget.id) + if (index > -1) { + mockBudgets[index] = { ...mockBudgets[index]!, ...budget, updatedAt: new Date().toISOString() } + return mockBudgets[index] + } + } else { + const newBudget: BudgetRecord = { + ...budget as BudgetRecord, + id: Math.max(...mockBudgets.map(i => i.id)) + 1, + status: 'draft', + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString() + } + mockBudgets.unshift(newBudget) + return newBudget + } + return null +} + +// 财务概览 +export async function mockGetFinanceOverview(): Promise { + await delay(400) + + const now = new Date() + const currentMonth = now.getMonth() + const currentYear = now.getFullYear() + + // 本月收入 + const monthlyIncomes = mockIncomes.filter(i => { + const date = new Date(i.createdAt) + return date.getMonth() === currentMonth && date.getFullYear() === currentYear + }) + const monthlyIncome = monthlyIncomes.reduce((sum, i) => sum + i.receivedAmount, 0) + + // 本月支出 + const monthlyExpensesList = mockExpenses.filter(e => { + const date = new Date(e.createdAt) + return date.getMonth() === currentMonth && date.getFullYear() === currentYear && e.status === 'paid' + }) + const monthlyExpense = monthlyExpensesList.reduce((sum, e) => sum + e.amount, 0) + + // 本年收入 + const yearlyIncomes = mockIncomes.filter(i => new Date(i.createdAt).getFullYear() === currentYear) + const yearlyIncome = yearlyIncomes.reduce((sum, i) => sum + i.receivedAmount, 0) + + // 本年支出 + const yearlyExpensesList = mockExpenses.filter(e => + new Date(e.createdAt).getFullYear() === currentYear && e.status === 'paid' + ) + const yearlyExpense = yearlyExpensesList.reduce((sum, e) => sum + e.amount, 0) + + // 账户余额 + const corporateBalance = mockAccounts.filter(a => a.type === 'corporate').reduce((sum, a) => sum + a.balance, 0) + const merchantBalance = mockAccounts.filter(a => a.type === 'merchant').reduce((sum, a) => sum + a.balance, 0) + + // 待处理事项 + const pendingReimbursements = mockReimbursements.filter(r => r.status === 'pending').length + const pendingExpenses = mockExpenses.filter(e => e.status === 'pending').length + const overdueReceivables = mockIncomes.filter(i => i.status === 'overdue').length + + // 趋势数据(最近6个月) + const incomesTrend: { date: string; amount: number }[] = [] + const expensesTrend: { date: string; amount: number }[] = [] + + for (let i = 5; i >= 0; i--) { + const date = new Date(currentYear, currentMonth - i, 1) + const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}` + + const incomeAmount = mockIncomes + .filter(inc => { + const d = new Date(inc.createdAt) + return d.getMonth() === date.getMonth() && d.getFullYear() === date.getFullYear() + }) + .reduce((sum, inc) => sum + inc.receivedAmount, 0) + + const expenseAmount = mockExpenses + .filter(exp => { + const d = new Date(exp.createdAt) + return d.getMonth() === date.getMonth() && d.getFullYear() === date.getFullYear() && exp.status === 'paid' + }) + .reduce((sum, exp) => sum + exp.amount, 0) + + incomesTrend.push({ date: dateStr, amount: incomeAmount }) + expensesTrend.push({ date: dateStr, amount: expenseAmount }) + } + + return { + monthlyIncome, + monthlyExpense, + monthlyProfit: monthlyIncome - monthlyExpense, + yearlyIncome, + yearlyExpense, + yearlyProfit: yearlyIncome - yearlyExpense, + totalBalance: corporateBalance + merchantBalance, + corporateBalance, + merchantBalance, + pendingReimbursements, + pendingExpenses, + overdueReceivables, + incomesTrend, + expensesTrend + } +} + +// 分类统计 +export async function mockGetIncomeByCategory(): Promise { + await delay(200) + + const categories: Record = {} + const total = mockIncomes.reduce((sum, i) => sum + i.receivedAmount, 0) + + mockIncomes.forEach(income => { + if (!categories[income.type]) { + categories[income.type] = { amount: 0, count: 0 } + } + categories[income.type]!.amount += income.receivedAmount + categories[income.type]!.count++ + }) + + return Object.entries(categories).map(([category, data]) => ({ + category, + amount: data.amount, + percentage: total > 0 ? Math.round((data.amount / total) * 100) : 0, + count: data.count + })) +} + +export async function mockGetExpenseByCategory(): Promise { + await delay(200) + + const categories: Record = {} + const paidExpenses = mockExpenses.filter(e => e.status === 'paid') + const total = paidExpenses.reduce((sum, e) => sum + e.amount, 0) + + paidExpenses.forEach(expense => { + if (!categories[expense.type]) { + categories[expense.type] = { amount: 0, count: 0 } + } + categories[expense.type]!.amount += expense.amount + categories[expense.type]!.count++ + }) + + return Object.entries(categories).map(([category, data]) => ({ + category, + amount: data.amount, + percentage: total > 0 ? Math.round((data.amount / total) * 100) : 0, + count: data.count + })) +} diff --git a/src/mock/index.ts b/src/mock/index.ts new file mode 100644 index 0000000..b4df0a5 --- /dev/null +++ b/src/mock/index.ts @@ -0,0 +1,7 @@ +/** + * Mock数据统一导出 + */ +export * from './user' +export * from './finance' +export * from './approval' +export * from './projects' diff --git a/src/mock/projects.ts b/src/mock/projects.ts new file mode 100644 index 0000000..bd38d4f --- /dev/null +++ b/src/mock/projects.ts @@ -0,0 +1,204 @@ +/** + * 项目管理 Mock 数据 + * + * 用于模拟平台管理中的项目和菜单配置 + */ + +// 菜单项类型 +export interface ProjectMenuItem { + key: string + label: string + icon?: string // 图标名称 + path?: string + children?: ProjectMenuItem[] +} + +// 项目版本类型 +export interface ProjectVersion { + id: string + version: string // 版本号,如 1.0.0 + description: string // 版本描述 + createdAt: string // 创建时间 + createdBy: string // 创建人 + // 版本快照数据 + snapshot: { + name: string + shortName: string + logo: string + color?: string + description?: string + baseUrl?: string + menus: ProjectMenuItem[] + } +} + +// 平台项目类型 +export interface PlatformProject { + id: string + name: string + shortName: string + logo: string + color?: string + description?: string + enabled: boolean + menuCount: number + createdAt: string + // 项目的访问地址(用于iframe嵌套) + baseUrl?: string + // 项目的菜单配置 + menus: ProjectMenuItem[] + // 当前版本号 + currentVersion?: string + // 版本历史 + versions?: ProjectVersion[] + // 新增字段 + group?: string // 分组 + domain?: string // 域名 + port?: number // 端口 + alias?: string // 代号 + enableHttps?: boolean // 启用HTTPS + acmeAccount?: string // Acme账户 + certificate?: string // 证书 + serverAddress?: string // 服务器地址 + remark?: string // 备注 +} + +// CodePort 的菜单配置 +const codePortMenus: ProjectMenuItem[] = [ + { + key: 'codePort-dashboard', + label: '控制台', + icon: 'DashboardOutlined', + path: '/dashboard' + }, + { + key: 'codePort-community', + label: '社区管理', + icon: 'TeamOutlined', + children: [ + { key: 'codePort-posts', label: '帖子管理', path: '/community/posts' }, + { key: 'codePort-comments', label: '评论管理', path: '/community/comments' }, + { key: 'codePort-tags', label: '标签管理', path: '/community/tags' }, + { key: 'codePort-circles', label: '城市圈子', path: '/community/circles' } + ] + }, + { + key: 'codePort-content', + label: '内容管理', + icon: 'ReadOutlined', + children: [ + { key: 'codePort-articles', label: '文章管理', path: '/content/articles' } + ] + }, + { + key: 'codePort-support', + label: '客服管理', + icon: 'CustomerServiceOutlined', + children: [ + { key: 'codePort-support-console', label: '接入会话', path: '/support/console' }, + { key: 'codePort-support-conversations', label: '会话列表', path: '/support/conversations' } + ] + }, + { + key: 'codePort-project', + label: '项目管理', + icon: 'ProjectOutlined', + children: [ + { key: 'codePort-projects', label: '项目列表', path: '/project/list' }, + { key: 'codePort-recruitment', label: '招募管理', path: '/project/recruitment' }, + { key: 'codePort-signed-projects', label: '已成交项目', path: '/project/signed' }, + { key: 'codePort-contracts', label: '合同管理', path: '/project/contract' }, + { key: 'codePort-sessions', label: '会话管理', path: '/project/sessions' } + ] + }, + { + key: 'codePort-talent', + label: '人才管理', + icon: 'IdcardOutlined', + children: [ + { key: 'codePort-talent-list', label: '人才列表', path: '/talent' }, + { key: 'codePort-resume-templates', label: '简历模板', path: '/talent/resume-templates' } + ] + }, + { + key: 'codePort-user', + label: '用户管理', + icon: 'UserOutlined', + children: [ + { key: 'codePort-users', label: '用户列表', path: '/user/list' }, + { key: 'codePort-certification', label: '认证管理', path: '/user/certification' }, + { key: 'codePort-roles', label: '角色管理', path: '/user/roles' }, + { key: 'codePort-positions', label: '岗位管理', path: '/user/positions' }, + { key: 'codePort-levels', label: '等级配置', path: '/user/levels' } + ] + } +] + +// Mock 项目数据 - 包含 codePort 项目 +export const mockProjects: PlatformProject[] = [ + { + id: 'codePort', + name: 'CodePort 码头', + shortName: 'CodePort', + logo: '码', + color: '#1890ff', + description: '人才外包平台管理后台', + enabled: true, + menuCount: 25, + createdAt: '2024-01-01T00:00:00Z', + baseUrl: 'http://localhost:5174', + menus: codePortMenus, + currentVersion: '1.0.0', + versions: [ + { + id: 'v1', + version: '1.0.0', + description: '初始版本', + createdAt: '2024-01-01T00:00:00Z', + createdBy: '管理员', + snapshot: { + name: 'CodePort 码头', + shortName: 'CodePort', + logo: '码', + color: '#1890ff', + description: '人才外包平台管理后台', + baseUrl: 'http://localhost:5174', + menus: codePortMenus + } + } + ] + } +] + +/** + * 获取所有启用的项目 + */ +export function getEnabledProjects(): PlatformProject[] { + return mockProjects.filter(p => p.enabled) +} + +/** + * 根据ID获取项目 + */ +export function getProjectById(id: string): PlatformProject | undefined { + return mockProjects.find(p => p.id === id) +} + +/** + * 获取项目的菜单配置 + */ +export function getProjectMenus(projectId: string): ProjectMenuItem[] { + const project = getProjectById(projectId) + return project?.menus || [] +} + +/** + * 生成新版本号 + */ +export function generateNextVersion(currentVersion?: string): string { + if (!currentVersion) return '1.0.0' + + const parts = currentVersion.split('.').map(Number) + parts[2] = (parts[2] || 0) + 1 + return parts.join('.') +} diff --git a/src/mock/user.ts b/src/mock/user.ts new file mode 100644 index 0000000..e2e771d --- /dev/null +++ b/src/mock/user.ts @@ -0,0 +1,144 @@ +/** + * 用户相关模拟数据 - 框架版 + */ +import type { + LoginParams, + LoginResult, + CaptchaResult, + UserInfo +} from '@/types' + +// 模拟验证码 +const captchaCodes: Map = new Map() + +// 模拟用户数据 +const mockUsers: Array<{ username: string; password: string; userInfo: UserInfo }> = [ + { + username: 'admin', + password: '123456', + userInfo: { + id: 1, + username: 'admin', + nickname: '超级管理员', + avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=admin', + email: 'admin@example.com', + phone: '13800138000', + role: 'admin', + permissions: ['*'], + createTime: '2024-01-01 00:00:00', + lastLoginTime: new Date().toLocaleString() + } + }, + { + username: 'user', + password: '123456', + userInfo: { + id: 2, + username: 'user', + nickname: '普通用户', + avatar: 'https://api.dicebear.com/7.x/avataaars/svg?seed=user', + email: 'user@example.com', + phone: '13900139000', + role: 'user', + permissions: ['read'], + createTime: '2024-06-01 00:00:00', + lastLoginTime: new Date().toLocaleString() + } + } +] + +// 生成随机验证码 +function generateCaptcha(): string { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' + let code = '' + for (let i = 0; i < 4; i++) { + code += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return code +} + +// 生成SVG验证码图片 +function generateCaptchaSvg(code: string): string { + const colors = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae', '#1890ff'] + let svg = `` + svg += `` + + // 添加干扰线 + for (let i = 0; i < 4; i++) { + const x1 = Math.random() * 120 + const y1 = Math.random() * 40 + const x2 = Math.random() * 120 + const y2 = Math.random() * 40 + const color = colors[Math.floor(Math.random() * colors.length)] + svg += `` + } + + // 添加验证码文字 + for (let i = 0; i < code.length; i++) { + const x = 15 + i * 25 + const y = 28 + Math.random() * 6 - 3 + const rotate = Math.random() * 30 - 15 + const color = colors[Math.floor(Math.random() * colors.length)] + svg += `${code[i]}` + } + + svg += `` + return `data:image/svg+xml;base64,${btoa(svg)}` +} + +// 模拟延迟 +function delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +// 获取验证码 +export async function mockGetCaptcha(): Promise { + await delay(300) + const captchaKey = 'captcha_' + Date.now() + const code = generateCaptcha() + captchaCodes.set(captchaKey, code) + + // 5分钟后过期 + setTimeout(() => captchaCodes.delete(captchaKey), 5 * 60 * 1000) + + return { + captchaKey, + captchaImage: generateCaptchaSvg(code) + } +} + +// 登录 +export async function mockLogin(params: LoginParams): Promise { + await delay(500) + + // 验证验证码 + const storedCode = captchaCodes.get(params.captchaKey) + if (!storedCode || storedCode.toUpperCase() !== params.captcha.toUpperCase()) { + throw new Error('验证码错误') + } + + // 验证用户 + const user = mockUsers.find( + u => u.username === params.username && u.password === params.password + ) + + if (!user) { + throw new Error('用户名或密码错误') + } + + // 删除已使用的验证码 + captchaCodes.delete(params.captchaKey) + + return { + token: 'mock_token_' + Date.now(), + refreshToken: 'mock_refresh_token_' + Date.now(), + expires: Date.now() + 24 * 60 * 60 * 1000, + userInfo: user.userInfo + } +} + +// 获取用户信息 +export async function mockGetUserInfo(): Promise { + await delay(200) + return mockUsers[0]!.userInfo +} diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..70baec2 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,256 @@ +/** + * 路由配置 - 框架版 + */ +import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router' + +const routes: RouteRecordRaw[] = [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/login/index.vue'), + meta: { + title: '登录', + requiresAuth: false + } + }, + + { + path: '/', + component: () => import('@/layouts/MainLayout.vue'), + redirect: '/finance/overview', + children: [ + // 财务管理 + { + path: 'finance/overview', + name: 'FinanceOverview', + component: () => import('@/views/finance/overview/index.vue'), + meta: { + title: '财务总览', + requiresAuth: true + } + }, + { + path: 'finance/income', + name: 'Income', + component: () => import('@/views/finance/income/index.vue'), + meta: { + title: '收入管理', + requiresAuth: true + } + }, + { + path: 'finance/expense', + name: 'Expense', + component: () => import('@/views/finance/expense/index.vue'), + meta: { + title: '支出管理', + requiresAuth: true + } + }, + { + path: 'finance/reimbursement', + name: 'Reimbursement', + component: () => import('@/views/finance/reimbursement/index.vue'), + meta: { + title: '报销管理', + requiresAuth: true + } + }, + { + path: 'finance/settlement', + name: 'Settlement', + component: () => import('@/views/finance/settlement/index.vue'), + meta: { + title: '结算管理', + requiresAuth: true + } + }, + { + path: 'finance/invoice', + name: 'Invoice', + component: () => import('@/views/finance/invoice/index.vue'), + meta: { + title: '发票管理', + requiresAuth: true + } + }, + { + path: 'finance/accounts', + name: 'FinanceAccounts', + component: () => import('@/views/finance/accounts/index.vue'), + meta: { + title: '账户管理', + requiresAuth: true + } + }, + { + path: 'finance/budget', + name: 'Budget', + component: () => import('@/views/finance/budget/index.vue'), + meta: { + title: '预算管理', + requiresAuth: true + } + }, + { + path: 'finance/reports', + name: 'FinanceReports', + component: () => import('@/views/finance/reports/index.vue'), + meta: { + title: '财务报表', + requiresAuth: true + } + }, + { + path: 'finance/import', + name: 'FinanceImport', + component: () => import('@/views/finance/import/index.vue'), + meta: { + title: '数据导入', + requiresAuth: true + } + }, + { + path: 'finance/advanced-reports', + name: 'AdvancedReports', + component: () => import('@/views/finance/advanced-reports/index.vue'), + meta: { + title: '高级报表', + requiresAuth: true + } + }, + + // 平台管理 + { + path: 'platform/projects', + name: 'PlatformProjects', + component: () => import('@/views/platform/projects/index.vue'), + meta: { + title: '项目管理', + requiresAuth: true + } + }, + { + path: 'platform/menus', + name: 'PlatformMenus', + component: () => import('@/views/platform/menus/index.vue'), + meta: { + title: '菜单管理', + requiresAuth: true + } + }, + { + path: 'platform/certificates', + name: 'PlatformCertificates', + component: () => import('@/views/platform/certificates/index.vue'), + meta: { + title: '证书管理', + requiresAuth: true + } + }, + { + path: 'platform/upload/:projectId?', + name: 'PlatformUpload', + component: () => import('@/views/platform/upload/index.vue'), + meta: { + title: '上传文件', + requiresAuth: true + } + }, + + // 系统设置 + { + path: 'settings', + name: 'Settings', + component: () => import('@/views/settings/index.vue'), + meta: { + title: '系统设置', + requiresAuth: true + } + }, + { + path: 'settings/dict', + name: 'Dict', + component: () => import('@/views/settings/dict/index.vue'), + meta: { + title: '字典管理', + requiresAuth: true + } + }, + { + path: 'settings/city', + name: 'City', + component: () => import('@/views/settings/city/index.vue'), + meta: { + title: '城市管理', + requiresAuth: true + } + }, + + // 审批流程管理 + { + path: 'system/approval', + name: 'ApprovalFlow', + component: () => import('@/views/system/approval/index.vue'), + meta: { + title: '审批流程', + requiresAuth: true + } + }, + { + path: 'system/approval-instances', + name: 'ApprovalInstances', + component: () => import('@/views/system/approval/instances.vue'), + meta: { + title: '审批记录', + requiresAuth: true + } + }, + + // 子项目 iframe 嵌套路由 + { + path: 'app/:projectId/:pathMatch(.*)*', + name: 'SubProjectPage', + component: () => import('@/views/app/IframePage.vue'), + meta: { + title: '子项目', + requiresAuth: true + } + } + ] + }, + // 404页面 + { + path: '/:pathMatch(.*)*', + name: 'NotFound', + component: () => import('@/views/error/404.vue'), + meta: { + title: '页面不存在' + } + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +// 路由守卫 +router.beforeEach((to, _from, next) => { + // 设置页面标题 + document.title = `${to.meta.title || '管理后台'} - 后台管理系统` + + const token = localStorage.getItem('token') + + if (to.meta.requiresAuth && !token) { + // 需要登录但未登录,跳转到登录页 + next({ path: '/login', query: { redirect: to.fullPath } }) + } else if (to.path === '/login' && token) { + // 已登录访问登录页,跳转到首页 + next({ path: '/' }) + } else { + next() + } +}) + +export default router diff --git a/src/stores/index.ts b/src/stores/index.ts new file mode 100644 index 0000000..f913055 --- /dev/null +++ b/src/stores/index.ts @@ -0,0 +1,12 @@ +/** + * Pinia Store 统一导出 + */ +import { createPinia } from 'pinia' + +const pinia = createPinia() + +export default pinia + +export * from './user' +export * from './project' + diff --git a/src/stores/project.ts b/src/stores/project.ts new file mode 100644 index 0000000..3bb33a5 --- /dev/null +++ b/src/stores/project.ts @@ -0,0 +1,292 @@ +/** + * 项目状态管理 + * + * 管理平台中的业务项目及当前选中的子项目状态 + */ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { + mockProjects as initialProjects, + generateNextVersion, + type PlatformProject, + type ProjectMenuItem, + type ProjectVersion +} from '@/mock/projects' + +export const useProjectStore = defineStore('project', () => { + // 所有项目列表(响应式) + const projects = ref([...initialProjects]) + + // 当前选中的项目ID + const currentProjectId = ref(null) + + // 所有启用的项目列表 + const enabledProjects = computed(() => projects.value.filter(p => p.enabled)) + + // 当前选中的项目 + const currentProject = computed(() => { + if (!currentProjectId.value) return null + return projects.value.find(p => p.id === currentProjectId.value) || null + }) + + // 当前项目的菜单 + const currentProjectMenus = computed(() => { + return currentProject.value?.menus || [] + }) + + // 当前项目的 baseUrl + const currentProjectBaseUrl = computed(() => { + return currentProject.value?.baseUrl || '' + }) + + // 是否处于子项目模式 + const isInSubProject = computed(() => !!currentProjectId.value) + + /** + * 切换到指定项目 + */ + function switchProject(projectId: string | null) { + currentProjectId.value = projectId + } + + /** + * 退出子项目,返回框架 + */ + function exitSubProject() { + currentProjectId.value = null + } + + /** + * 添加新项目 + */ + function addProject(project: PlatformProject) { + // 为新项目创建初始版本 + const initialVersion: ProjectVersion = { + id: `v-${Date.now()}`, + version: '1.0.0', + description: '初始版本', + createdAt: new Date().toISOString(), + createdBy: '管理员', + snapshot: { + name: project.name, + shortName: project.shortName, + logo: project.logo, + color: project.color, + description: project.description, + baseUrl: project.baseUrl, + menus: project.menus || [] + } + } + + project.currentVersion = '1.0.0' + project.versions = [initialVersion] + + projects.value.push(project) + } + + /** + * 更新项目 + */ + function updateProject(projectId: string, updates: Partial) { + const index = projects.value.findIndex(p => p.id === projectId) + if (index > -1) { + projects.value[index] = { + ...projects.value[index], + ...updates + } + } + } + + /** + * 删除项目 + */ + function deleteProject(projectId: string) { + const index = projects.value.findIndex(p => p.id === projectId) + if (index > -1) { + projects.value.splice(index, 1) + } + // 如果删除的是当前项目,退出子项目模式 + if (currentProjectId.value === projectId) { + currentProjectId.value = null + } + } + + /** + * 切换项目状态 + */ + function toggleProjectEnabled(projectId: string) { + const project = projects.value.find(p => p.id === projectId) + if (project) { + project.enabled = !project.enabled + } + } + + /** + * 根据ID获取项目 + */ + function getProjectById(projectId: string): PlatformProject | undefined { + return projects.value.find(p => p.id === projectId) + } + + /** + * 创建新版本 + */ + function createVersion(projectId: string, description: string): ProjectVersion | null { + const project = projects.value.find(p => p.id === projectId) + if (!project) return null + + const newVersion = generateNextVersion(project.currentVersion) + const versionRecord: ProjectVersion = { + id: `v-${Date.now()}`, + version: newVersion, + description, + createdAt: new Date().toISOString(), + createdBy: '管理员', + snapshot: { + name: project.name, + shortName: project.shortName, + logo: project.logo, + color: project.color, + description: project.description, + baseUrl: project.baseUrl, + menus: JSON.parse(JSON.stringify(project.menus)) // 深拷贝 + } + } + + if (!project.versions) { + project.versions = [] + } + project.versions.push(versionRecord) + project.currentVersion = newVersion + + return versionRecord + } + + /** + * 回退到指定版本 + */ + function rollbackToVersion(projectId: string, versionId: string): boolean { + const project = projects.value.find(p => p.id === projectId) + if (!project || !project.versions) return false + + const version = project.versions.find(v => v.id === versionId) + if (!version) return false + + // 恢复快照数据 + const { snapshot } = version + project.name = snapshot.name + project.shortName = snapshot.shortName + project.logo = snapshot.logo + project.color = snapshot.color + project.description = snapshot.description + project.baseUrl = snapshot.baseUrl + project.menus = JSON.parse(JSON.stringify(snapshot.menus)) // 深拷贝 + project.currentVersion = version.version + + return true + } + + /** + * 获取项目的版本列表 + */ + function getProjectVersions(projectId: string): ProjectVersion[] { + const project = projects.value.find(p => p.id === projectId) + return project?.versions || [] + } + + /** + * 删除指定版本(不能删除当前版本) + */ + function deleteVersion(projectId: string, versionId: string): boolean { + const project = projects.value.find(p => p.id === projectId) + if (!project || !project.versions) return false + + const versionIndex = project.versions.findIndex(v => v.id === versionId) + if (versionIndex === -1) return false + + const version = project.versions[versionIndex] + // 不能删除当前版本 + if (version.version === project.currentVersion) return false + + project.versions.splice(versionIndex, 1) + return true + } + + /** + * 根据菜单key获取完整路由路径 + * 返回格式: /app/{projectId}/{path} + */ + function getMenuRoutePath(menuKey: string): string | null { + if (!currentProject.value) return null + + const projectId = currentProject.value.id + const menus = currentProject.value.menus + + // 递归查找菜单项 + function findMenuPath(items: ProjectMenuItem[]): string | null { + for (const item of items) { + if (item.key === menuKey && item.path) { + return `/app/${projectId}${item.path}` + } + if (item.children) { + const found = findMenuPath(item.children) + if (found) return found + } + } + return null + } + + return findMenuPath(menus) + } + + /** + * 获取菜单项到路由的映射 + */ + function getMenuRouteMap(): Record { + if (!currentProject.value) return {} + + const projectId = currentProject.value.id + const map: Record = {} + + function processMenus(items: ProjectMenuItem[]) { + for (const item of items) { + if (item.path) { + map[item.key] = `/app/${projectId}${item.path}` + } + if (item.children) { + processMenus(item.children) + } + } + } + + processMenus(currentProject.value.menus) + return map + } + + return { + // 状态 + projects, + currentProjectId, + currentProject, + currentProjectMenus, + currentProjectBaseUrl, + enabledProjects, + isInSubProject, + // 方法 + switchProject, + exitSubProject, + addProject, + updateProject, + deleteProject, + toggleProjectEnabled, + getProjectById, + // 版本管理方法 + createVersion, + rollbackToVersion, + getProjectVersions, + deleteVersion, + // 菜单路由方法 + getMenuRoutePath, + getMenuRouteMap + } +}) diff --git a/src/stores/user.ts b/src/stores/user.ts new file mode 100644 index 0000000..9891cdf --- /dev/null +++ b/src/stores/user.ts @@ -0,0 +1,117 @@ +/** + * 用户状态管理 + */ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import type { UserInfo, LoginParams, LoginResult, CaptchaResult } from '@/types' +import { mockGetCaptcha, mockLogin, mockGetUserInfo } from '@/mock' + +// 是否使用Mock数据 +const USE_MOCK = true + +export const useUserStore = defineStore('user', () => { + // 状态 + const token = ref(localStorage.getItem('token') || '') + const userInfo = ref(null) + + // 计算属性 + const isLoggedIn = computed(() => !!token.value) + const username = computed(() => userInfo.value?.username || '') + const nickname = computed(() => userInfo.value?.nickname || userInfo.value?.username || '') + const avatar = computed(() => userInfo.value?.avatar || '') + + // 获取验证码 + async function getCaptcha(): Promise { + if (USE_MOCK) { + return await mockGetCaptcha() + } + // 真实API调用 + const { request } = await import('@/utils/request') + const res = await request.get('/auth/captcha') + return res.data.data + } + + // 登录 + async function login(params: LoginParams): Promise { + let data: LoginResult + + if (USE_MOCK) { + data = await mockLogin(params) + } else { + // 真实API调用 + const { request } = await import('@/utils/request') + const res = await request.post('/auth/login', params) + data = res.data.data + } + + // 保存token + token.value = data.token + localStorage.setItem('token', data.token) + + // 保存用户信息 + userInfo.value = data.userInfo + + return data + } + + // 登出 + async function logout(): Promise { + try { + if (!USE_MOCK) { + const { request } = await import('@/utils/request') + await request.post('/auth/logout') + } + } catch (error) { + console.error('登出请求失败:', error) + } finally { + // 清除本地状态 + token.value = '' + userInfo.value = null + localStorage.removeItem('token') + } + } + + // 获取用户信息 + async function getUserInfo(): Promise { + if (USE_MOCK) { + const info = await mockGetUserInfo() + userInfo.value = info + return info + } + const { request } = await import('@/utils/request') + const res = await request.get('/user/info') + userInfo.value = res.data.data + return res.data.data + } + + // 设置用户信息 + function setUserInfo(info: UserInfo) { + userInfo.value = info + } + + // 重置状态 + function resetState() { + token.value = '' + userInfo.value = null + localStorage.removeItem('token') + } + + return { + // 状态 + token, + userInfo, + // 计算属性 + isLoggedIn, + username, + nickname, + avatar, + // 方法 + getCaptcha, + login, + logout, + getUserInfo, + setUserInfo, + resetState + } +}) + diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..8c7991e --- /dev/null +++ b/src/style.css @@ -0,0 +1,48 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body { + width: 100%; + height: 100%; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + line-height: 1.5; + color: #333; + background-color: #f0f2f5; +} + +#app { + width: 100%; + height: 100%; +} + +a { + color: #1890ff; + text-decoration: none; +} + +a:hover { + color: #40a9ff; +} + +/* 滚动条样式 */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +::-webkit-scrollbar-track { + background: #f1f1f1; +} diff --git a/src/types/approval.ts b/src/types/approval.ts new file mode 100644 index 0000000..fe58a80 --- /dev/null +++ b/src/types/approval.ts @@ -0,0 +1,199 @@ +/** + * 审批流程管理类型定义 + */ + +// 审批人选择方式 +export type ApproverType = 'specified' | 'role' | 'superior' | 'self_select' + +// 审批人类型映射 +export const ApproverTypeMap: Record = { + specified: '指定人员', + role: '按角色', + superior: '上级领导', + self_select: '发起人自选' +} + +// 审批方式 +export type ApprovalMode = 'and' | 'or' + +// 审批方式映射 +export const ApprovalModeMap: Record = { + and: '会签(所有人通过)', + or: '或签(任一人通过)' +} + +// 审批节点 +export interface ApprovalNode { + id: number + name: string // 节点名称 + approverType: ApproverType // 审批人选择方式 + approverIds?: number[] // 指定审批人ID列表(approverType为specified时使用) + approverRole?: string // 审批角色(approverType为role时使用) + approvalMode: ApprovalMode // 审批方式 + timeoutHours?: number // 超时时间(小时) + timeoutAction?: 'skip' | 'reject' // 超时操作 + order: number // 节点顺序 +} + +// 审批流程适用场景 +export type ApprovalScenario = + | 'project_publish' + | 'withdrawal' + | 'contract' + | 'certification' + | 'content' + // 财务管理审批场景 + | 'expense_reimbursement' // 费用报销 + | 'payment_request' // 付款申请 + | 'purchase_request' // 采购申请 + | 'budget_adjustment' // 预算调整 + | 'invoice_apply' // 发票申请 + +// 场景映射 +export const ApprovalScenarioMap: Record = { + project_publish: '项目发布', + withdrawal: '提现申请', + contract: '合同签署', + certification: '用户认证', + content: '内容审核', + // 财务管理 + expense_reimbursement: '费用报销', + payment_request: '付款申请', + purchase_request: '采购申请', + budget_adjustment: '预算调整', + invoice_apply: '发票申请' +} + +// 审批流程模板 +export interface ApprovalTemplate { + id: number + name: string // 模板名称 + description?: string // 描述 + scenario: ApprovalScenario // 适用场景 + nodes: ApprovalNode[] // 审批节点列表 + enabled: boolean // 是否启用 + createdAt: string + updatedAt: string +} + +// 审批实例状态 +export type ApprovalInstanceStatus = 'pending' | 'in_progress' | 'approved' | 'rejected' | 'withdrawn' | 'cancelled' + +// 实例状态映射 +export const ApprovalInstanceStatusMap: Record = { + pending: '待提交', + in_progress: '审批中', + approved: '已通过', + rejected: '已拒绝', + withdrawn: '已撤回', + cancelled: '已取消' +} + +// 实例状态徽章 +export const ApprovalInstanceStatusBadgeMap: Record = { + pending: 'default', + in_progress: 'processing', + approved: 'success', + rejected: 'error', + withdrawn: 'warning', + cancelled: 'default' +} + +// 节点审批记录 +export interface NodeApprovalRecord { + nodeId: number + nodeName: string + approverId: number + approverName: string + approverAvatar: string + action: 'approve' | 'reject' | 'transfer' | 'return' + comment?: string + operatedAt: string +} + +// 审批实例 +export interface ApprovalInstance { + id: number + templateId: number + templateName: string + scenario: ApprovalScenario + + // 业务关联 + businessType: string // 业务类型 + businessId: number // 业务ID + businessTitle: string // 业务标题 + + // 发起人 + initiatorId: number + initiatorName: string + initiatorAvatar: string + + // 状态 + status: ApprovalInstanceStatus + currentNodeId?: number // 当前节点ID + currentNodeName?: string // 当前节点名称 + + // 审批记录 + records: NodeApprovalRecord[] + + // 时间 + submittedAt: string + completedAt?: string + createdAt: string +} + +// 审批人信息(用于选择) +export interface ApproverInfo { + id: number + name: string + avatar: string + role: string + department?: string +} + +// 模板查询参数 +export interface ApprovalTemplateQueryParams { + page?: number + pageSize?: number + keyword?: string + scenario?: ApprovalScenario + enabled?: boolean +} + +// 模板列表结果 +export interface ApprovalTemplateListResult { + list: ApprovalTemplate[] + total: number + page: number + pageSize: number +} + +// 实例查询参数 +export interface ApprovalInstanceQueryParams { + page?: number + pageSize?: number + keyword?: string + scenario?: ApprovalScenario + status?: ApprovalInstanceStatus + startDate?: string + endDate?: string +} + +// 实例列表结果 +export interface ApprovalInstanceListResult { + list: ApprovalInstance[] + total: number + page: number + pageSize: number +} + +// 统计 +export interface ApprovalStats { + totalTemplates: number + enabledTemplates: number + totalInstances: number + pendingInstances: number + inProgressInstances: number + approvedInstances: number + rejectedInstances: number +} diff --git a/src/types/finance.ts b/src/types/finance.ts new file mode 100644 index 0000000..ba25b3c --- /dev/null +++ b/src/types/finance.ts @@ -0,0 +1,946 @@ +/** + * 财务管理相关类型定义 + */ + +// ==================== 基础类型 ==================== + +// 货币金额类型(以分为单位存储,显示时转换) +export type MoneyAmount = number + +// 通用状态 +export type CommonStatus = 'active' | 'inactive' + +// ==================== 结算管理(原有) ==================== + +// 结算状态 +export type SettlementStatus = 'pending' | 'paying' | 'completed' | 'rejected' + +export const SettlementStatusMap: Record = { + pending: '待审核', + paying: '打款中', + completed: '已完成', + rejected: '已驳回' +} + +export const SettlementStatusColorMap: Record = { + pending: 'orange', + paying: 'cyan', + completed: 'green', + rejected: 'red' +} + +// 结算单 +export interface SettlementItem { + id: number + projectId: number + projectName: string + talentId: number + talentName: string + period: string // 结算周期/月份 + + // 金额相关 + totalAmount: number // 项目总额 / 本期对账金额 + platformFee: number // 平台服务费 + taxableAmount: number // 应纳税所得额 (Total - Fee) + taxRate: number // 税率 (0.xx) + taxAmount: number // 扣税金额 + actualAmount: number // 实发金额 + + status: SettlementStatus + invoiceStatus: 'none' | 'pending' | 'received' // 发票状态 + + bankInfo?: { + accountName: string + accountNo: string + bankName: string + } + + createdAt: string + auditTime?: string + paymentTime?: string + remark?: string +} + +// 发票类型 +export type InvoiceType = 'vat_special' | 'vat_normal' | 'personal' + +export const InvoiceTypeMap: Record = { + vat_special: '增值税专用发票', + vat_normal: '增值税普通发票', + personal: '个人完税证明' // 或者是个人代开 +} + +// 发票记录 +export interface InvoiceItem { + id: number + settlementId: number // 关联结算单 + type: InvoiceType + title: string // 抬头 + taxCode?: string // 税号(企业) + amount: number + fileUrl?: string // 电子发票文件链接 + status: 'pending' | 'issued' | 'rejected' + submitTime: string + issueTime?: string // 开票时间 + rejectReason?: string +} + +export interface SettlementQueryParams { + page?: number + pageSize?: number + keyword?: string + status?: SettlementStatus + dateRange?: [string, string] +} + +// ==================== 账户管理 ==================== + +// 账户类型 +export type AccountType = 'corporate' | 'merchant' + +export const AccountTypeMap: Record = { + corporate: '对公账户', + merchant: '商户号' +} + +// 商户号平台 +export type MerchantPlatform = 'wechat' | 'alipay' | 'unionpay' | 'other' + +export const MerchantPlatformMap: Record = { + wechat: '微信支付', + alipay: '支付宝', + unionpay: '银联', + other: '其他' +} + +// 银行/支付账户 +export interface FinanceAccount { + id: number + name: string // 账户名称 + type: AccountType + + // 对公账户信息 + bankName?: string + bankBranch?: string // 开户支行 + accountNo?: string + + // 商户号信息 + merchantId?: string + merchantPlatform?: MerchantPlatform + appId?: string + + balance: number // 当前余额 + status: CommonStatus + isDefault: boolean + remark?: string + createdAt: string + updatedAt: string +} + +// ==================== 收入管理 ==================== + +// 收入类型 +export type IncomeType = 'project' | 'service_fee' | 'consulting' | 'commission' | 'other' + +export const IncomeTypeMap: Record = { + project: '项目收入', + service_fee: '平台服务费', + consulting: '咨询服务费', + commission: '佣金收入', + other: '其他收入' +} + +export const IncomeTypeColorMap: Record = { + project: 'blue', + service_fee: 'green', + consulting: 'purple', + commission: 'cyan', + other: 'default' +} + +// 收款状态 +export type IncomeStatus = 'pending' | 'partial' | 'received' | 'overdue' + +export const IncomeStatusMap: Record = { + pending: '待收款', + partial: '部分收款', + received: '已收款', + overdue: '已逾期' +} + +export const IncomeStatusColorMap: Record = { + pending: 'orange', + partial: 'cyan', + received: 'green', + overdue: 'red' +} + +// 收入记录 +export interface IncomeRecord { + id: number + incomeNo: string // 收入编号 + type: IncomeType + title: string // 收入名称/描述 + + // 客户信息 + customerId?: number + customerName: string + customerContact?: string + + // 关联信息 + projectId?: number + projectName?: string + contractNo?: string + + // 金额信息 + totalAmount: number // 总金额 + receivedAmount: number // 已收金额 + pendingAmount: number // 待收金额 + + // 收款账户 + accountId: number + accountName: string + + status: IncomeStatus + expectedDate?: string // 预计收款日期 + actualDate?: string // 实际收款日期 + + // 发票信息 + invoiceRequired: boolean + invoiceIssued: boolean + invoiceNo?: string + + remark?: string + createdBy: string + createdAt: string + updatedAt: string +} + +// 收款记录(一笔收入可能分多次收款) +export interface IncomePayment { + id: number + incomeId: number + amount: number + paymentMethod: 'bank' | 'wechat' | 'alipay' | 'cash' | 'other' + accountId: number + transactionNo?: string // 交易流水号 + paymentDate: string + remark?: string + createdBy: string + createdAt: string +} + +// ==================== 支出管理 ==================== + +// 支出类型 +export type ExpenseType = + | 'salary' // 工资 + | 'office' // 办公费 + | 'rent' // 房租水电 + | 'travel' // 差旅费 + | 'marketing' // 市场推广 + | 'equipment' // 设备采购 + | 'service' // 外包服务 + | 'tax' // 税费 + | 'social_insurance' // 社保公积金 + | 'other' // 其他 + +export const ExpenseTypeMap: Record = { + salary: '工资薪酬', + office: '办公费用', + rent: '房租水电', + travel: '差旅费用', + marketing: '市场推广', + equipment: '设备采购', + service: '外包服务', + tax: '税费', + social_insurance: '社保公积金', + other: '其他支出' +} + +export const ExpenseTypeColorMap: Record = { + salary: 'blue', + office: 'cyan', + rent: 'purple', + travel: 'orange', + marketing: 'magenta', + equipment: 'geekblue', + service: 'volcano', + tax: 'gold', + social_insurance: 'lime', + other: 'default' +} + +// 支出状态 +export type ExpenseStatus = 'draft' | 'pending' | 'approved' | 'paid' | 'rejected' + +export const ExpenseStatusMap: Record = { + draft: '草稿', + pending: '待审批', + approved: '已批准', + paid: '已支付', + rejected: '已驳回' +} + +export const ExpenseStatusColorMap: Record = { + draft: 'default', + pending: 'orange', + approved: 'cyan', + paid: 'green', + rejected: 'red' +} + +// 支出记录 +export interface ExpenseRecord { + id: number + expenseNo: string // 支出编号 + type: ExpenseType + title: string + + // 收款方信息 + payeeName: string + payeeAccount?: string + payeeBankName?: string + + // 金额信息 + amount: number + + // 关联信息 + projectId?: number + projectName?: string + departmentId?: number + departmentName?: string + + // 付款账户 + accountId?: number + accountName?: string + + status: ExpenseStatus + + // 审批信息 + approvalId?: number // 关联审批流程实例ID + approvalStatus?: string + + // 附件/凭证 + attachments?: string[] + + paymentDate?: string + remark?: string + createdBy: string + createdAt: string + updatedAt: string +} + +// ==================== 报销管理 ==================== + +// 报销类型 +export type ReimbursementType = + | 'travel' // 差旅报销 + | 'meal' // 餐饮报销 + | 'transport' // 交通报销 + | 'communication' // 通讯报销 + | 'office' // 办公用品 + | 'other' // 其他 + +export const ReimbursementTypeMap: Record = { + travel: '差旅报销', + meal: '餐饮报销', + transport: '交通报销', + communication: '通讯报销', + office: '办公用品', + other: '其他报销' +} + +export const ReimbursementTypeColorMap: Record = { + travel: 'blue', + meal: 'orange', + transport: 'cyan', + communication: 'purple', + office: 'green', + other: 'default' +} + +// 报销状态 +export type ReimbursementStatus = 'draft' | 'pending' | 'approved' | 'paid' | 'rejected' + +export const ReimbursementStatusMap: Record = { + draft: '草稿', + pending: '审批中', + approved: '已批准', + paid: '已打款', + rejected: '已驳回' +} + +export const ReimbursementStatusColorMap: Record = { + draft: 'default', + pending: 'orange', + approved: 'cyan', + paid: 'green', + rejected: 'red' +} + +// 报销单 +export interface ReimbursementRecord { + id: number + reimbursementNo: string // 报销单号 + type: ReimbursementType + title: string + + // 申请人信息 + applicantId: number + applicantName: string + departmentId?: number + departmentName?: string + + // 金额 + totalAmount: number + + // 明细 + items: ReimbursementItem[] + + status: ReimbursementStatus + + // 审批信息 + approvalId?: number + approvalStatus?: string + currentApprover?: string + + // 收款信息 + bankAccountName?: string + bankAccountNo?: string + bankName?: string + + // 打款信息 + paymentAccountId?: number + paymentDate?: string + paymentRemark?: string + + remark?: string + createdAt: string + updatedAt: string +} + +// 报销明细项 +export interface ReimbursementItem { + id: number + type: ReimbursementType + description: string + amount: number + occurDate: string // 费用发生日期 + attachments?: string[] // 凭证/发票图片 +} + +// ==================== 预算管理 ==================== + +// 预算周期 +export type BudgetPeriod = 'monthly' | 'quarterly' | 'yearly' + +export const BudgetPeriodMap: Record = { + monthly: '月度', + quarterly: '季度', + yearly: '年度' +} + +// 预算状态 +export type BudgetStatus = 'draft' | 'active' | 'completed' | 'cancelled' + +export const BudgetStatusMap: Record = { + draft: '草稿', + active: '执行中', + completed: '已完成', + cancelled: '已取消' +} + +// 预算记录 +export interface BudgetRecord { + id: number + name: string + period: BudgetPeriod + year: number + quarter?: number // 1-4 + month?: number // 1-12 + + // 部门/项目 + departmentId?: number + departmentName?: string + projectId?: number + projectName?: string + + // 预算明细 + items: BudgetItem[] + + totalBudget: number // 总预算 + usedAmount: number // 已使用 + remainingAmount: number // 剩余 + usageRate: number // 使用率 (0-100) + + status: BudgetStatus + remark?: string + createdBy: string + createdAt: string + updatedAt: string +} + +// 预算明细项(按费用类型) +export interface BudgetItem { + expenseType: ExpenseType + budgetAmount: number + usedAmount: number + remainingAmount: number +} + +// ==================== 财务统计 ==================== + +// 财务概览数据 +export interface FinanceOverview { + // 本月数据 + monthlyIncome: number + monthlyExpense: number + monthlyProfit: number + + // 本年数据 + yearlyIncome: number + yearlyExpense: number + yearlyProfit: number + + // 账户余额 + totalBalance: number + corporateBalance: number + merchantBalance: number + + // 待处理事项 + pendingReimbursements: number + pendingExpenses: number + overdueReceivables: number + + // 趋势数据 + incomesTrend: TrendItem[] + expensesTrend: TrendItem[] +} + +export interface TrendItem { + date: string // YYYY-MM + amount: number + label?: string +} + +// 收支分类统计 +export interface CategoryStats { + category: string + amount: number + percentage: number + count: number +} + +// ==================== 查询参数 ==================== + +export interface IncomeQueryParams { + page?: number + pageSize?: number + keyword?: string + type?: IncomeType + status?: IncomeStatus + accountId?: number + dateRange?: [string, string] +} + +export interface ExpenseQueryParams { + page?: number + pageSize?: number + keyword?: string + type?: ExpenseType + status?: ExpenseStatus + accountId?: number + dateRange?: [string, string] +} + +export interface ReimbursementQueryParams { + page?: number + pageSize?: number + keyword?: string + type?: ReimbursementType + status?: ReimbursementStatus + applicantId?: number + dateRange?: [string, string] +} + +export interface BudgetQueryParams { + page?: number + pageSize?: number + period?: BudgetPeriod + year?: number + departmentId?: number + status?: BudgetStatus +} + +// ==================== 多级审批流程 ==================== + +// 审批节点类型 +export type ApprovalNodeType = 'approver' | 'cc' | 'condition' + +// 审批节点状态 +export type ApprovalNodeStatus = 'pending' | 'approved' | 'rejected' | 'skipped' + +export const ApprovalNodeStatusMap: Record = { + pending: '待审批', + approved: '已通过', + rejected: '已驳回', + skipped: '已跳过' +} + +export const ApprovalNodeStatusColorMap: Record = { + pending: 'orange', + approved: 'green', + rejected: 'red', + skipped: 'default' +} + +// 财务审批节点 (与 approval.ts 中的 ApprovalNode 不同,这是用于财务多级审批的节点) +export interface FinanceApprovalNode { + id: number + nodeType: ApprovalNodeType + nodeName: string + + // 审批人信息 + approverId?: number + approverName?: string + approverRole?: string // 按角色审批时使用 + + // 条件节点使用 + condition?: { + field: 'amount' | 'type' | 'department' + operator: '>' | '<' | '=' | '>=' | '<=' + value: number | string + } + + // 节点状态 + status: ApprovalNodeStatus + remark?: string + actionTime?: string + + // 节点顺序 + order: number +} + +// 审批流程实例 +export interface ApprovalFlowInstance { + id: number + flowTemplateId: number // 关联审批流程模板 + flowTemplateName: string + + // 关联业务 + businessType: 'expense' | 'reimbursement' | 'contract' | 'payment' + businessId: number + businessNo: string + businessTitle: string + + // 申请人 + applicantId: number + applicantName: string + applicantDepartment?: string + + // 审批金额 + amount: number + + // 审批节点 + nodes: FinanceApprovalNode[] + currentNodeIndex: number + + // 整体状态 + status: 'pending' | 'approved' | 'rejected' | 'cancelled' + + createdAt: string + completedAt?: string +} + +// 审批流程模板(用于配置多级审批) +export interface ApprovalFlowTemplate { + id: number + name: string + businessType: 'expense' | 'reimbursement' | 'contract' | 'payment' + description?: string + + // 触发条件 + triggerConditions?: { + minAmount?: number + maxAmount?: number + departments?: number[] + } + + // 节点配置 + nodeConfigs: { + nodeType: ApprovalNodeType + nodeName: string + approverType: 'user' | 'role' | 'department_head' | 'applicant_superior' + approverId?: number + approverRole?: string + condition?: FinanceApprovalNode['condition'] + }[] + + status: CommonStatus + createdAt: string + updatedAt: string +} + +// ==================== 数据导入 ==================== + +// 导入状态 +export type ImportStatus = 'pending' | 'processing' | 'completed' | 'failed' + +export const ImportStatusMap: Record = { + pending: '待处理', + processing: '导入中', + completed: '已完成', + failed: '导入失败' +} + +// 导入记录类型 +export type ImportDataType = 'income' | 'expense' | 'reimbursement' + +export const ImportDataTypeMap: Record = { + income: '收入记录', + expense: '支出记录', + reimbursement: '报销记录' +} + +// 导入记录 +export interface ImportRecord { + id: number + + // 文件信息 + fileName: string + fileSize: number // bytes + fileUrl: string + + // 导入类型 + dataType: ImportDataType + + // 导入结果 + status: ImportStatus + totalRows: number + successRows: number + failedRows: number + + // 错误信息 + errors?: { + row: number + field: string + message: string + originalValue?: string + }[] + + // 操作信息 + importedBy: string + createdAt: string + completedAt?: string +} + +// 导入数据预览行 +export interface ImportPreviewRow { + rowIndex: number + data: Record + isValid: boolean + errors?: string[] +} + +// ==================== 高级财务报表 ==================== + +// 资产负债表项 +export interface BalanceSheetItem { + code: string // 科目编码 + name: string // 科目名称 + level: number // 层级 1-3 + parentCode?: string + + // 金额 + endingBalance: number // 期末余额 + beginningBalance: number // 期初余额 + + // 类型 + category: 'asset' | 'liability' | 'equity' +} + +// 资产负债表 +export interface BalanceSheet { + reportDate: string // 报表日期 YYYY-MM-DD + + // 资产 + assets: { + // 流动资产 + current: { + cash: number // 货币资金 + receivables: number // 应收账款 + prepayments: number // 预付账款 + inventory: number // 存货 + otherCurrent: number // 其他流动资产 + totalCurrent: number // 流动资产合计 + } + // 非流动资产 + nonCurrent: { + fixedAssets: number // 固定资产 + intangibleAssets: number // 无形资产 + longTermInvestments: number // 长期投资 + otherNonCurrent: number // 其他非流动资产 + totalNonCurrent: number // 非流动资产合计 + } + total: number // 资产总计 + } + + // 负债 + liabilities: { + // 流动负债 + current: { + shortTermLoans: number // 短期借款 + payables: number // 应付账款 + advanceReceipts: number // 预收账款 + taxesPayable: number // 应交税费 + otherCurrent: number // 其他流动负债 + totalCurrent: number // 流动负债合计 + } + // 非流动负债 + nonCurrent: { + longTermLoans: number // 长期借款 + otherNonCurrent: number // 其他非流动负债 + totalNonCurrent: number // 非流动负债合计 + } + total: number // 负债合计 + } + + // 所有者权益 + equity: { + paidInCapital: number // 实收资本(股本) + capitalReserve: number // 资本公积 + surplusReserve: number // 盈余公积 + undistributedProfit: number // 未分配利润 + total: number // 所有者权益合计 + } + + // 负债和所有者权益总计 + totalLiabilitiesAndEquity: number +} + +// 现金流量表项分类 +export type CashFlowCategory = 'operating' | 'investing' | 'financing' + +// 现金流量表 +export interface CashFlowStatement { + reportPeriod: string // 报表期间 YYYY-MM + + // 经营活动产生的现金流量 + operating: { + // 流入 + inflows: { + salesGoods: number // 销售商品、提供劳务收到的现金 + taxRefunds: number // 收到的税费返还 + otherOperating: number // 收到其他与经营活动有关的现金 + totalInflows: number // 经营活动现金流入小计 + } + // 流出 + outflows: { + purchaseGoods: number // 购买商品、接受劳务支付的现金 + employeePayments: number // 支付给职工以及为职工支付的现金 + taxes: number // 支付的各项税费 + otherOperating: number // 支付其他与经营活动有关的现金 + totalOutflows: number // 经营活动现金流出小计 + } + netCashFlow: number // 经营活动产生的现金流量净额 + } + + // 投资活动产生的现金流量 + investing: { + inflows: { + investmentReturns: number // 收回投资收到的现金 + investmentIncome: number // 取得投资收益收到的现金 + disposalAssets: number // 处置固定资产等收回的现金 + otherInvesting: number // 收到其他与投资活动有关的现金 + totalInflows: number + } + outflows: { + purchaseAssets: number // 购建固定资产等支付的现金 + investments: number // 投资支付的现金 + otherInvesting: number // 支付其他与投资活动有关的现金 + totalOutflows: number + } + netCashFlow: number // 投资活动产生的现金流量净额 + } + + // 筹资活动产生的现金流量 + financing: { + inflows: { + capitalContributions: number // 吸收投资收到的现金 + borrowings: number // 取得借款收到的现金 + otherFinancing: number // 收到其他与筹资活动有关的现金 + totalInflows: number + } + outflows: { + debtRepayments: number // 偿还债务支付的现金 + dividends: number // 分配股利、利润支付的现金 + otherFinancing: number // 支付其他与筹资活动有关的现金 + totalOutflows: number + } + netCashFlow: number // 筹资活动产生的现金流量净额 + } + + // 汇率变动对现金的影响 + exchangeRateEffect: number + + // 现金及现金等价物净增加额 + netCashIncrease: number + + // 期初现金余额 + beginningCash: number + + // 期末现金余额 + endingCash: number +} + +// 利润表 +export interface IncomeStatement { + reportPeriod: string // 报表期间 YYYY-MM + + // 收入 + revenue: { + mainBusiness: number // 主营业务收入 + otherBusiness: number // 其他业务收入 + total: number // 营业收入合计 + } + + // 成本 + costs: { + mainBusiness: number // 主营业务成本 + otherBusiness: number // 其他业务成本 + total: number // 营业成本合计 + } + + // 毛利润 + grossProfit: number + + // 期间费用 + periodExpenses: { + selling: number // 销售费用 + administrative: number // 管理费用 + financial: number // 财务费用 + researchDevelopment: number // 研发费用 + total: number + } + + // 营业利润 + operatingProfit: number + + // 营业外收支 + nonOperating: { + income: number // 营业外收入 + expenses: number // 营业外支出 + net: number + } + + // 利润总额 + totalProfit: number + + // 所得税费用 + incomeTax: number + + // 净利润 + netProfit: number +} + diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..ebb13a6 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,9 @@ +/** + * 类型统一导出 + */ + +export * from './user' +export * from './response' +export * from './finance' +export * from './approval' +export * from './upload' diff --git a/src/types/response.ts b/src/types/response.ts new file mode 100644 index 0000000..74dc377 --- /dev/null +++ b/src/types/response.ts @@ -0,0 +1,33 @@ +/** + * 响应相关类型定义 + */ + +// 基础响应结构 +export interface ApiResponse { + code: number + message: string + data: T + success: boolean +} + +// 分页请求参数 +export interface PageParams { + page: number + pageSize: number +} + +// 分页响应数据 +export interface PageResult { + list: T[] + total: number + page: number + pageSize: number + totalPages: number +} + +// 列表响应 +export interface ListResult { + list: T[] + total: number +} + diff --git a/src/types/upload.ts b/src/types/upload.ts new file mode 100644 index 0000000..6b8eb8b --- /dev/null +++ b/src/types/upload.ts @@ -0,0 +1,28 @@ +/** + * 上传相关类型定义 + */ + +/** + * 上传文件类型 + */ +export interface UploadFile { + uid: string + name: string + path: string + size: number + type: string + status: 'pending' | 'uploading' | 'done' | 'error' + percent: number + file: File +} + +/** + * 重复文件类型 + */ +export interface DuplicateFile { + uid: string + name: string + path: string + size: number + file: File +} diff --git a/src/types/user.ts b/src/types/user.ts new file mode 100644 index 0000000..3730d67 --- /dev/null +++ b/src/types/user.ts @@ -0,0 +1,39 @@ +/** + * 用户相关类型定义 + */ + +// 用户信息(用于鉴权等场景) +export interface UserInfo { + id: number + username: string + nickname: string + avatar?: string + email?: string + phone?: string + role: string + permissions?: string[] + createTime?: string + lastLoginTime?: string +} + +// 登录请求参数 +export interface LoginParams { + username: string + password: string + captcha: string + captchaKey: string +} + +// 登录响应数据 +export interface LoginResult { + token: string + refreshToken?: string + expires: number + userInfo: UserInfo +} + +// 验证码响应 +export interface CaptchaResult { + captchaKey: string + captchaImage: string +} diff --git a/src/utils/common.ts b/src/utils/common.ts new file mode 100644 index 0000000..77d700c --- /dev/null +++ b/src/utils/common.ts @@ -0,0 +1,77 @@ +/** + * 公共工具函数 + */ +import type { Key } from 'ant-design-vue/es/table/interface' +import type { TablePaginationConfig } from 'ant-design-vue' + +/** + * 格式化日期 + */ +export function formatDate(str: string): string { + return new Date(str).toLocaleDateString('zh-CN') +} + +/** + * 格式化日期时间 + */ +export function formatDateTime(str: string): string { + return new Date(str).toLocaleString('zh-CN') +} + +/** + * 格式化数字(超过1000显示为x.xk) + */ +export function formatCount(num: number): string { + if (num >= 1000) { + return (num / 1000).toFixed(1) + 'k' + } + return num.toString() +} + +/** + * 从数组中随机取一个元素(带类型安全) + */ +export function randomItem(arr: T[]): T { + return arr[Math.floor(Math.random() * arr.length)]! +} + +/** + * 模拟延迟 + */ +export function delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)) +} + +/** + * 表格选择变更处理器类型 + */ +export type TableSelectChange = (keys: Key[]) => void + +/** + * 表格分页变更处理器类型 + */ +export type TablePaginationChange = (pagination: TablePaginationConfig) => void + +/** + * 创建表格分页变更处理函数 + */ +export function createTableChangeHandler( + paginationRef: { current: number; pageSize: number }, + loadFn: () => void +) { + return (pagination: TablePaginationConfig) => { + paginationRef.current = pagination.current || 1 + paginationRef.pageSize = pagination.pageSize || 10 + loadFn() + } +} + +/** + * 创建表格选择变更处理函数 + */ +export function createSelectChangeHandler(selectedKeysRef: { value: Key[] }) { + return (keys: Key[]) => { + selectedKeysRef.value = keys + } +} + diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..5bb138c --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,112 @@ +/** + * Axios 基础配置 + */ +import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios' +import { message } from 'ant-design-vue' +import type { ApiResponse } from '@/types' + +// 创建axios实例 +const service: AxiosInstance = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL || '/api', + timeout: 30000, + headers: { + 'Content-Type': 'application/json' + } +}) + +// 请求拦截器 +service.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + // 从localStorage获取token + const token = localStorage.getItem('token') + if (token && config.headers) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + console.error('请求错误:', error) + return Promise.reject(error) + } +) + +// 响应拦截器 +service.interceptors.response.use( + (response: AxiosResponse) => { + const res = response.data + + // 根据业务状态码判断请求是否成功 + if (res.code === 200 || res.success) { + return response + } + + // 处理业务错误 + message.error(res.message || '请求失败') + + // 处理特定错误码 + if (res.code === 401) { + // token过期或未授权,跳转登录 + localStorage.removeItem('token') + window.location.href = '/login' + } + + return Promise.reject(new Error(res.message || '请求失败')) + }, + (error) => { + console.error('响应错误:', error) + + // 处理HTTP错误 + let errorMessage = '网络错误,请稍后重试' + if (error.response) { + const { status } = error.response + switch (status) { + case 400: + errorMessage = '请求参数错误' + break + case 401: + errorMessage = '未授权,请重新登录' + localStorage.removeItem('token') + window.location.href = '/login' + break + case 403: + errorMessage = '拒绝访问' + break + case 404: + errorMessage = '请求资源不存在' + break + case 500: + errorMessage = '服务器内部错误' + break + default: + errorMessage = `请求失败(${status})` + } + } else if (error.code === 'ECONNABORTED') { + errorMessage = '请求超时,请稍后重试' + } + + message.error(errorMessage) + return Promise.reject(error) + } +) + +// 封装请求方法 +export const request = { + get(url: string, config?: AxiosRequestConfig): Promise>> { + return service.get(url, config) + }, + + post(url: string, data?: unknown, config?: AxiosRequestConfig): Promise>> { + return service.post(url, data, config) + }, + + put(url: string, data?: unknown, config?: AxiosRequestConfig): Promise>> { + return service.put(url, data, config) + }, + + delete(url: string, config?: AxiosRequestConfig): Promise>> { + return service.delete(url, config) + } +} + +export default service + diff --git a/src/views/app/IframePage.vue b/src/views/app/IframePage.vue new file mode 100644 index 0000000..29eea17 --- /dev/null +++ b/src/views/app/IframePage.vue @@ -0,0 +1,134 @@ +