commit 10b6d0099a5b381cfa2d7f89eeb3fed20adcccf1 Author: panchengyong Date: Fri Feb 27 23:50:25 2026 +0800 潘的第一次 commit diff --git a/erp-frontend-vue/.gitignore b/erp-frontend-vue/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/erp-frontend-vue/.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/erp-frontend-vue/.vscode/extensions.json b/erp-frontend-vue/.vscode/extensions.json new file mode 100644 index 0000000..a7cea0b --- /dev/null +++ b/erp-frontend-vue/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar"] +} diff --git a/erp-frontend-vue/README.md b/erp-frontend-vue/README.md new file mode 100644 index 0000000..33895ab --- /dev/null +++ b/erp-frontend-vue/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/erp-frontend-vue/package-lock.json b/erp-frontend-vue/package-lock.json new file mode 100644 index 0000000..f40ccbc --- /dev/null +++ b/erp-frontend-vue/package-lock.json @@ -0,0 +1,2764 @@ +{ + "name": "erp-frontend-vue", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true + }, + "@asamuzakjp/css-color": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", + "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", + "dev": true, + "requires": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.4" + } + }, + "@asamuzakjp/dom-selector": { + "version": "6.7.6", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz", + "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==", + "dev": true, + "requires": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.4" + } + }, + "@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true + }, + "@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==" + }, + "@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==" + }, + "@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "requires": { + "@babel/types": "^7.28.6" + } + }, + "@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "requires": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + } + }, + "@bufbuild/protobuf": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz", + "integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==", + "dev": true + }, + "@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true + }, + "@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true + }, + "@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "requires": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + } + }, + "@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true + }, + "@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.26.tgz", + "integrity": "sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==", + "dev": true + }, + "@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true + }, + "@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==" + }, + "@element-plus/icons-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", + "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==" + }, + "@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "dev": true, + "optional": true + }, + "@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "dev": true, + "optional": true + }, + "@exodus/bytes": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.9.0.tgz", + "integrity": "sha512-lagqsvnk09NKogQaN/XrtlWeUF8SRhT12odMvbTIIaVObqzwAogL6jhR4DAp0gPuKoM1AOVrKUshJpRdpMFrww==", + "dev": true + }, + "@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "requires": { + "@floating-ui/utils": "^0.2.10" + } + }, + "@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "requires": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==" + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==" + }, + "@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, + "@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "optional": true, + "requires": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6", + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + } + }, + "@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "dev": true, + "optional": true + }, + "@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "dev": true, + "optional": true + }, + "@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "dev": true, + "optional": true + }, + "@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "dev": true, + "optional": true + }, + "@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "dev": true, + "optional": true + }, + "@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "dev": true, + "optional": true + }, + "@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "dev": true, + "optional": true + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz", + "integrity": "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz", + "integrity": "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz", + "integrity": "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz", + "integrity": "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==", + "dev": true, + "optional": true + }, + "@rollup/rollup-freebsd-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz", + "integrity": "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-freebsd-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz", + "integrity": "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz", + "integrity": "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz", + "integrity": "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz", + "integrity": "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz", + "integrity": "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-loong64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz", + "integrity": "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-loong64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz", + "integrity": "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-ppc64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz", + "integrity": "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-ppc64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz", + "integrity": "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz", + "integrity": "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz", + "integrity": "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz", + "integrity": "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz", + "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz", + "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-openbsd-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz", + "integrity": "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-openharmony-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz", + "integrity": "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz", + "integrity": "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz", + "integrity": "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz", + "integrity": "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz", + "integrity": "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==", + "dev": true, + "optional": true + }, + "@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true + }, + "@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "requires": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true + }, + "@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "@types/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==" + }, + "@types/lodash-es": { + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", + "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", + "requires": { + "@types/lodash": "*" + } + }, + "@types/node": { + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", + "requires": { + "undici-types": "~7.16.0" + } + }, + "@types/qrcode": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", + "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", + "requires": { + "@types/node": "*" + } + }, + "@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==" + }, + "@vitejs/plugin-vue": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.3.tgz", + "integrity": "sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==", + "dev": true, + "requires": { + "@rolldown/pluginutils": "1.0.0-beta.53" + } + }, + "@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "requires": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + } + }, + "@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "requires": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "dependencies": { + "estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0" + } + } + } + }, + "@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "requires": { + "tinyrainbow": "^3.0.3" + } + }, + "@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "requires": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + } + }, + "@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "requires": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + } + }, + "@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true + }, + "@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "requires": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + } + }, + "@volar/language-core": { + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.27.tgz", + "integrity": "sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==", + "dev": true, + "requires": { + "@volar/source-map": "2.4.27" + } + }, + "@volar/source-map": { + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.27.tgz", + "integrity": "sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==", + "dev": true + }, + "@volar/typescript": { + "version": "2.4.27", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.27.tgz", + "integrity": "sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==", + "dev": true, + "requires": { + "@volar/language-core": "2.4.27", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "@vue/compiler-core": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.27.tgz", + "integrity": "sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==", + "requires": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.27", + "entities": "^7.0.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "@vue/compiler-dom": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.27.tgz", + "integrity": "sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==", + "requires": { + "@vue/compiler-core": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "@vue/compiler-sfc": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.27.tgz", + "integrity": "sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==", + "requires": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.27", + "@vue/compiler-dom": "3.5.27", + "@vue/compiler-ssr": "3.5.27", + "@vue/shared": "3.5.27", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "@vue/compiler-ssr": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.27.tgz", + "integrity": "sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==", + "requires": { + "@vue/compiler-dom": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, + "@vue/language-core": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.2.3.tgz", + "integrity": "sha512-VpN/GnYDzGLh44AI6i1OB/WsLXo6vwnl0EWHBelGc4TyC0yEq6azwNaed/+Tgr8anFlSdWYnMEkyHJDPe7ii7A==", + "dev": true, + "requires": { + "@volar/language-core": "2.4.27", + "@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" + } + }, + "@vue/reactivity": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.27.tgz", + "integrity": "sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==", + "requires": { + "@vue/shared": "3.5.27" + } + }, + "@vue/runtime-core": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.27.tgz", + "integrity": "sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==", + "requires": { + "@vue/reactivity": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "@vue/runtime-dom": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.27.tgz", + "integrity": "sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==", + "requires": { + "@vue/reactivity": "3.5.27", + "@vue/runtime-core": "3.5.27", + "@vue/shared": "3.5.27", + "csstype": "^3.2.3" + } + }, + "@vue/server-renderer": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.27.tgz", + "integrity": "sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==", + "requires": { + "@vue/compiler-ssr": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "@vue/shared": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.27.tgz", + "integrity": "sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==" + }, + "@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "dev": true, + "requires": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "@vue/tsconfig": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.8.1.tgz", + "integrity": "sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==", + "dev": true + }, + "@vueuse/core": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz", + "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", + "requires": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.1", + "@vueuse/shared": "10.11.1", + "vue-demi": ">=0.14.8" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==" + } + } + }, + "@vueuse/metadata": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz", + "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==" + }, + "@vueuse/shared": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz", + "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==", + "requires": { + "vue-demi": ">=0.14.8" + }, + "dependencies": { + "vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==" + } + } + }, + "abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true + }, + "agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true + }, + "alien-signals": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.1.2.tgz", + "integrity": "sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==", + "dev": true + }, + "ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true + }, + "assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true + }, + "async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.3.tgz", + "integrity": "sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "requires": { + "require-from-string": "^2.0.2" + } + }, + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true + }, + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "optional": true, + "requires": { + "readdirp": "^4.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colorjs.io": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz", + "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + }, + "config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "requires": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + } + }, + "cssstyle": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", + "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", + "dev": true, + "requires": { + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.4" + } + }, + "csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" + }, + "data-urls": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.1.tgz", + "integrity": "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==", + "dev": true, + "requires": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^15.1.0" + }, + "dependencies": { + "whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true + } + } + }, + "dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==" + }, + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + }, + "decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "optional": true + }, + "dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==" + }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "requires": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + } + }, + "element-plus": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.13.1.tgz", + "integrity": "sha512-eG4BDBGdAsUGN6URH1PixzZb0ngdapLivIk1meghS1uEueLvQ3aljSKrCt5x6sYb6mUk8eGtzTQFgsPmLavQcA==", + "requires": { + "@ctrl/tinycolor": "^3.4.1", + "@element-plus/icons-vue": "^2.3.2", + "@floating-ui/dom": "^1.0.1", + "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", + "@types/lodash": "^4.17.20", + "@types/lodash-es": "^4.17.12", + "@vueuse/core": "^10.11.0", + "async-validator": "^4.2.5", + "dayjs": "^1.11.19", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "lodash-unified": "^1.0.3", + "memoize-one": "^6.0.0", + "normalize-wheel-es": "^1.2.0" + }, + "dependencies": { + "@popperjs/core": { + "version": "npm:@sxzz/popperjs-es@2.11.8", + "resolved": "https://registry.npmjs.org/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz", + "integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==" + } + } + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==" + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, + "esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true + }, + "fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" + }, + "foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + } + }, + "form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + } + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "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" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, + "glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "dependencies": { + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "requires": { + "@exodus/bytes": "^1.6.0" + } + }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + }, + "immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "requires": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + } + }, + "js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true + }, + "jsdom": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", + "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", + "dev": true, + "requires": { + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.6.0", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + }, + "lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + }, + "lodash-unified": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/lodash-unified/-/lodash-unified-1.0.3.tgz", + "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==" + }, + "lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true + }, + "magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, + "mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true + }, + "memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, + "nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==" + }, + "node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + }, + "nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "requires": { + "abbrev": "^2.0.0" + } + }, + "normalize-wheel-es": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", + "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==" + }, + "obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "requires": { + "entities": "^6.0.0" + }, + "dependencies": { + "entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true + } + } + }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + } + } + }, + "pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true + }, + "pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==" + }, + "postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "requires": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true + }, + "qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "requires": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + } + }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "optional": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "rollup": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz", + "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.56.0", + "@rollup/rollup-android-arm64": "4.56.0", + "@rollup/rollup-darwin-arm64": "4.56.0", + "@rollup/rollup-darwin-x64": "4.56.0", + "@rollup/rollup-freebsd-arm64": "4.56.0", + "@rollup/rollup-freebsd-x64": "4.56.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", + "@rollup/rollup-linux-arm-musleabihf": "4.56.0", + "@rollup/rollup-linux-arm64-gnu": "4.56.0", + "@rollup/rollup-linux-arm64-musl": "4.56.0", + "@rollup/rollup-linux-loong64-gnu": "4.56.0", + "@rollup/rollup-linux-loong64-musl": "4.56.0", + "@rollup/rollup-linux-ppc64-gnu": "4.56.0", + "@rollup/rollup-linux-ppc64-musl": "4.56.0", + "@rollup/rollup-linux-riscv64-gnu": "4.56.0", + "@rollup/rollup-linux-riscv64-musl": "4.56.0", + "@rollup/rollup-linux-s390x-gnu": "4.56.0", + "@rollup/rollup-linux-x64-gnu": "4.56.0", + "@rollup/rollup-linux-x64-musl": "4.56.0", + "@rollup/rollup-openbsd-x64": "4.56.0", + "@rollup/rollup-openharmony-arm64": "4.56.0", + "@rollup/rollup-win32-arm64-msvc": "4.56.0", + "@rollup/rollup-win32-ia32-msvc": "4.56.0", + "@rollup/rollup-win32-x64-gnu": "4.56.0", + "@rollup/rollup-win32-x64-msvc": "4.56.0", + "@types/estree": "1.0.8", + "fsevents": "~2.3.2" + } + }, + "rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "sass": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz", + "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==", + "dev": true, + "optional": true, + "requires": { + "@parcel/watcher": "^2.4.1", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-embedded": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.97.3.tgz", + "integrity": "sha512-eKzFy13Nk+IRHhlAwP3sfuv+PzOrvzUkwJK2hdoCKYcWGSdmwFpeGpWmyewdw8EgBnsKaSBtgf/0b2K635ecSA==", + "dev": true, + "requires": { + "@bufbuild/protobuf": "^2.5.0", + "colorjs.io": "^0.5.0", + "immutable": "^5.0.2", + "rxjs": "^7.4.0", + "sass-embedded-all-unknown": "1.97.3", + "sass-embedded-android-arm": "1.97.3", + "sass-embedded-android-arm64": "1.97.3", + "sass-embedded-android-riscv64": "1.97.3", + "sass-embedded-android-x64": "1.97.3", + "sass-embedded-darwin-arm64": "1.97.3", + "sass-embedded-darwin-x64": "1.97.3", + "sass-embedded-linux-arm": "1.97.3", + "sass-embedded-linux-arm64": "1.97.3", + "sass-embedded-linux-musl-arm": "1.97.3", + "sass-embedded-linux-musl-arm64": "1.97.3", + "sass-embedded-linux-musl-riscv64": "1.97.3", + "sass-embedded-linux-musl-x64": "1.97.3", + "sass-embedded-linux-riscv64": "1.97.3", + "sass-embedded-linux-x64": "1.97.3", + "sass-embedded-unknown-all": "1.97.3", + "sass-embedded-win32-arm64": "1.97.3", + "sass-embedded-win32-x64": "1.97.3", + "supports-color": "^8.1.1", + "sync-child-process": "^1.0.2", + "varint": "^6.0.0" + } + }, + "sass-embedded-all-unknown": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.97.3.tgz", + "integrity": "sha512-t6N46NlPuXiY3rlmG6/+1nwebOBOaLFOOVqNQOC2cJhghOD4hh2kHNQQTorCsbY9S1Kir2la1/XLBwOJfui0xg==", + "dev": true, + "optional": true, + "requires": { + "sass": "1.97.3" + } + }, + "sass-embedded-android-arm": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.97.3.tgz", + "integrity": "sha512-cRTtf/KV/q0nzGZoUzVkeIVVFv3L/tS1w4WnlHapphsjTXF/duTxI8JOU1c/9GhRPiMdfeXH7vYNcMmtjwX7jg==", + "dev": true, + "optional": true + }, + "sass-embedded-android-arm64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.97.3.tgz", + "integrity": "sha512-aiZ6iqiHsUsaDx0EFbbmmA0QgxicSxVVN3lnJJ0f1RStY0DthUkquGT5RJ4TPdaZ6ebeJWkboV4bra+CP766eA==", + "dev": true, + "optional": true + }, + "sass-embedded-android-riscv64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.97.3.tgz", + "integrity": "sha512-zVEDgl9JJodofGHobaM/q6pNETG69uuBIGQHRo789jloESxxZe82lI3AWJQuPmYCOG5ElfRthqgv89h3gTeLYA==", + "dev": true, + "optional": true + }, + "sass-embedded-android-x64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.97.3.tgz", + "integrity": "sha512-3ke0le7ZKepyXn/dKKspYkpBC0zUk/BMciyP5ajQUDy4qJwobd8zXdAq6kOkdiMB+d9UFJOmEkvgFJHl3lqwcw==", + "dev": true, + "optional": true + }, + "sass-embedded-darwin-arm64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.97.3.tgz", + "integrity": "sha512-fuqMTqO4gbOmA/kC5b9y9xxNYw6zDEyfOtMgabS7Mz93wimSk2M1quQaTJnL98Mkcsl2j+7shNHxIS/qpcIDDA==", + "dev": true, + "optional": true + }, + "sass-embedded-darwin-x64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.97.3.tgz", + "integrity": "sha512-b/2RBs/2bZpP8lMkyZ0Px0vkVkT8uBd0YXpOwK7iOwYkAT8SsO4+WdVwErsqC65vI5e1e5p1bb20tuwsoQBMVA==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-arm": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.97.3.tgz", + "integrity": "sha512-2lPQ7HQQg4CKsH18FTsj2hbw5GJa6sBQgDsls+cV7buXlHjqF8iTKhAQViT6nrpLK/e8nFCoaRgSqEC8xMnXuA==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-arm64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.97.3.tgz", + "integrity": "sha512-IP1+2otCT3DuV46ooxPaOKV1oL5rLjteRzf8ldZtfIEcwhSgSsHgA71CbjYgLEwMY9h4jeal8Jfv3QnedPvSjg==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-musl-arm": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.97.3.tgz", + "integrity": "sha512-cBTMU68X2opBpoYsSZnI321gnoaiMBEtc+60CKCclN6PCL3W3uXm8g4TLoil1hDD6mqU9YYNlVG6sJ+ZNef6Lg==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-musl-arm64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.97.3.tgz", + "integrity": "sha512-Lij0SdZCsr+mNRSyDZ7XtJpXEITrYsaGbOTz5e6uFLJ9bmzUbV7M8BXz2/cA7bhfpRPT7/lwRKPdV4+aR9Ozcw==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-musl-riscv64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.97.3.tgz", + "integrity": "sha512-sBeLFIzMGshR4WmHAD4oIM7WJVkSoCIEwutzptFtGlSlwfNiijULp+J5hA2KteGvI6Gji35apR5aWj66wEn/iA==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-musl-x64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.97.3.tgz", + "integrity": "sha512-/oWJ+OVrDg7ADDQxRLC/4g1+Nsz1g4mkYS2t6XmyMJKFTFK50FVI2t5sOdFH+zmMp+nXHKM036W94y9m4jjEcw==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-riscv64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.97.3.tgz", + "integrity": "sha512-l3IfySApLVYdNx0Kjm7Zehte1CDPZVcldma3dZt+TfzvlAEerM6YDgsk5XEj3L8eHBCgHgF4A0MJspHEo2WNfA==", + "dev": true, + "optional": true + }, + "sass-embedded-linux-x64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.97.3.tgz", + "integrity": "sha512-Kwqwc/jSSlcpRjULAOVbndqEy2GBzo6OBmmuBVINWUaJLJ8Kczz3vIsDUWLfWz/kTEw9FHBSiL0WCtYLVAXSLg==", + "dev": true, + "optional": true + }, + "sass-embedded-unknown-all": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.97.3.tgz", + "integrity": "sha512-/GHajyYJmvb0IABUQHbVHf1nuHPtIDo/ClMZ81IDr59wT5CNcMe7/dMNujXwWugtQVGI5UGmqXWZQCeoGnct8Q==", + "dev": true, + "optional": true, + "requires": { + "sass": "1.97.3" + } + }, + "sass-embedded-win32-arm64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.97.3.tgz", + "integrity": "sha512-RDGtRS1GVvQfMGAmVXNxYiUOvPzn9oO1zYB/XUM9fudDRnieYTcUytpNTQZLs6Y1KfJxgt5Y+giRceC92fT8Uw==", + "dev": true, + "optional": true + }, + "sass-embedded-win32-x64": { + "version": "1.97.3", + "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.97.3.tgz", + "integrity": "sha512-SFRa2lED9UEwV6vIGeBXeBOLKF+rowF3WmNfb/BzhxmdAsKofCXrJ8ePW7OcDVrvNEbTOGwhsReIsF5sH8fVaw==", + "dev": true, + "optional": true + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + }, + "source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" + }, + "stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "sync-child-process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz", + "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==", + "dev": true, + "requires": { + "sync-message-port": "^1.0.0" + } + }, + "sync-message-port": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz", + "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==", + "dev": true + }, + "tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true + }, + "tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true + }, + "tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "requires": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + } + }, + "tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true + }, + "tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", + "dev": true, + "requires": { + "tldts-core": "^7.0.19" + } + }, + "tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", + "dev": true + }, + "tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "dev": true, + "requires": { + "tldts": "^7.0.5" + } + }, + "tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "requires": { + "punycode": "^2.3.1" + } + }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true + }, + "undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + }, + "varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "dev": true + }, + "vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "requires": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "fsevents": "~2.3.3", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + } + }, + "vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "requires": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + } + }, + "vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true + }, + "vue": { + "version": "3.5.27", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.27.tgz", + "integrity": "sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==", + "requires": { + "@vue/compiler-dom": "3.5.27", + "@vue/compiler-sfc": "3.5.27", + "@vue/runtime-dom": "3.5.27", + "@vue/server-renderer": "3.5.27", + "@vue/shared": "3.5.27" + } + }, + "vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true + }, + "vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "requires": { + "@vue/devtools-api": "^6.6.4" + } + }, + "vue-tsc": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.2.3.tgz", + "integrity": "sha512-1RdRB7rQXGFMdpo0aXf9spVzWEPGAk7PEb/ejHQwVrcuQA/HsGiixIc3uBQeqY2YjeEEgvr2ShQewBgcN4c1Cw==", + "dev": true, + "requires": { + "@volar/typescript": "2.4.27", + "@vue/language-core": "3.2.3" + } + }, + "w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "requires": { + "xml-name-validator": "^5.0.0" + } + }, + "webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true + }, + "whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true + }, + "whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "requires": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" + }, + "why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "requires": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + }, + "ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true + }, + "xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/erp-frontend-vue/package.json b/erp-frontend-vue/package.json new file mode 100644 index 0000000..9ed57f3 --- /dev/null +++ b/erp-frontend-vue/package.json @@ -0,0 +1,34 @@ +{ + "name": "erp-frontend-vue", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@element-plus/icons-vue": "^2.3.2", + "@types/qrcode": "^1.5.6", + "axios": "^1.13.3", + "element-plus": "^2.13.1", + "qrcode": "^1.5.4", + "vue": "^3.5.24", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@types/node": "^24.10.1", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.8.1", + "jsdom": "^27.4.0", + "sass-embedded": "^1.97.3", + "typescript": "~5.9.3", + "vite": "^7.2.4", + "vitest": "^4.0.18", + "vue-tsc": "^3.1.4" + } +} diff --git a/erp-frontend-vue/public/vite.svg b/erp-frontend-vue/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/erp-frontend-vue/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/erp-frontend-vue/src/App.vue b/erp-frontend-vue/src/App.vue new file mode 100644 index 0000000..803d8f4 --- /dev/null +++ b/erp-frontend-vue/src/App.vue @@ -0,0 +1,9 @@ + + + + + diff --git a/erp-frontend-vue/src/api/__tests__/customer.test.ts b/erp-frontend-vue/src/api/__tests__/customer.test.ts new file mode 100644 index 0000000..82fac3e --- /dev/null +++ b/erp-frontend-vue/src/api/__tests__/customer.test.ts @@ -0,0 +1,156 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { + getCustomerList, + getCustomerDetail, + createCustomer, + updateCustomer, + deleteCustomer, + updateCustomerStatus, + type Customer, + type CustomerQuery +} from '../customer' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../request', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args) + } +})) + +describe('customer API', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + describe('getCustomerList', () => { + it('requests list and maps rows with compat fields', async () => { + mockGet.mockResolvedValue({ + rows: [ + { + clientId: 1, + clientCode: 'C001', + clientName: '客户A', + clientNick: 'A', + contact1: '张三', + contact1Tel: '13800001111', + address: '深圳市', + enableFlag: 'Y', + createTime: '2026-01-25 10:00:00' + } + ], + total: 1 + }) + + const res = await getCustomerList({ + clientCode: 'C001', + pageNum: 1, + pageSize: 10 + }) + + expect(mockGet).toHaveBeenCalledWith('/erp/sl/client/list', { + params: expect.objectContaining({ clientCode: 'C001', pageNum: 1, pageSize: 10 }) + }) + expect(res.list).toHaveLength(1) + expect(res.total).toBe(1) + expect(res.list[0]).toMatchObject({ + clientId: 1, + clientCode: 'C001', + clientName: '客户A', + id: 1, + code: 'C001', + name: '客户A', + contact: '张三', + phone: '13800001111' + }) + }) + + it('maps name param to clientName', async () => { + mockGet.mockResolvedValue({ rows: [], total: 0 }) + await getCustomerList({ name: '测试' } as CustomerQuery) + expect(mockGet).toHaveBeenCalledWith('/erp/sl/client/list', { + params: expect.objectContaining({ clientName: '测试' }) + }) + }) + + it('handles res.list or res.data.rows', async () => { + mockGet.mockResolvedValue({ list: [{ clientId: 2, clientCode: 'C002', clientName: 'B' }], total: 1 }) + const res = await getCustomerList({}) + expect(res.list).toHaveLength(1) + expect(res.list[0]?.clientCode).toBe('C002') + }) + }) + + describe('getCustomerDetail', () => { + it('fetches detail and maps compat fields', async () => { + mockGet.mockResolvedValue({ + data: { + clientId: 1, + clientCode: 'C001', + clientName: '客户A', + contact1: '李四', + contact1Tel: '13900002222' + } + }) + + const res = await getCustomerDetail(1) + expect(mockGet).toHaveBeenCalledWith('/erp/sl/client/1') + expect(res.clientId).toBe(1) + expect(res.id).toBe(1) + expect(res.code).toBe('C001') + expect(res.name).toBe('客户A') + expect(res.contact).toBe('李四') + expect(res.phone).toBe('13900002222') + }) + }) + + describe('createCustomer', () => { + it('POSTs payload', async () => { + mockPost.mockResolvedValue(undefined) + const data: Partial = { + clientCode: 'C003', + clientName: '客户C', + enableFlag: 'Y' + } + await createCustomer(data) + expect(mockPost).toHaveBeenCalledWith('/erp/sl/client', data) + }) + }) + + describe('updateCustomer', () => { + it('PUTs payload', async () => { + mockPut.mockResolvedValue(undefined) + const data: Partial = { clientId: 1, clientName: '更新名' } + await updateCustomer(data) + expect(mockPut).toHaveBeenCalledWith('/erp/sl/client', data) + }) + }) + + describe('deleteCustomer', () => { + it('DELETEs single id', async () => { + mockDelete.mockResolvedValue(undefined) + await deleteCustomer(1) + expect(mockDelete).toHaveBeenCalledWith('/erp/sl/client/1') + }) + + it('DELETEs comma-separated ids', async () => { + mockDelete.mockResolvedValue(undefined) + await deleteCustomer([1, 2, 3]) + expect(mockDelete).toHaveBeenCalledWith('/erp/sl/client/1,2,3') + }) + }) + + describe('updateCustomerStatus', () => { + it('PUTs clientId and enableFlag', async () => { + mockPut.mockResolvedValue(undefined) + await updateCustomerStatus(1, 'N') + expect(mockPut).toHaveBeenCalledWith('/erp/sl/client', { clientId: 1, enableFlag: 'N' }) + }) + }) +}) diff --git a/erp-frontend-vue/src/api/auth.ts b/erp-frontend-vue/src/api/auth.ts new file mode 100644 index 0000000..f71ad73 --- /dev/null +++ b/erp-frontend-vue/src/api/auth.ts @@ -0,0 +1,58 @@ +import axios from 'axios' + +// 创建一个专门用于登录相关的 axios 实例(不带 /erp 前缀) +const authRequest = axios.create({ + baseURL: '', + timeout: 30000 +}) + +// 登录方法 +export function login(username: string, password: string, code: string, uuid: string) { + return authRequest({ + url: '/login', + method: 'post', + data: { username, password, code, uuid } + }).then(res => res.data) +} + +// 注册方法 +export function register(data: { username: string; password: string; confirmPassword: string }) { + return authRequest({ + url: '/register', + method: 'post', + data + }).then(res => res.data) +} + +// 获取用户详细信息 +export function getInfo() { + const token = localStorage.getItem('Admin-Token') + return authRequest({ + url: '/getInfo', + method: 'get', + headers: { + Authorization: token ? `Bearer ${token}` : '' + } + }).then(res => res.data) +} + +// 退出方法 +export function logout() { + const token = localStorage.getItem('Admin-Token') + return authRequest({ + url: '/logout', + method: 'post', + headers: { + Authorization: token ? `Bearer ${token}` : '' + } + }).then(res => res.data) +} + +// 获取验证码 +export function getCodeImg() { + return authRequest({ + url: '/captchaImage', + method: 'get', + timeout: 20000 + }).then(res => res.data) +} diff --git a/erp-frontend-vue/src/api/checkin.ts b/erp-frontend-vue/src/api/checkin.ts new file mode 100644 index 0000000..818c51c --- /dev/null +++ b/erp-frontend-vue/src/api/checkin.ts @@ -0,0 +1,96 @@ +import request from './request' + +export interface CheckinLine { + lineId: number + checkinId: number + checkinCode: string + lineNo: number + orderLineId?: number + trackCode?: string + itemId: number + itemCode: string + itemName: string + specification?: string + unitName?: string + orderQuantity?: number + quantity: number + stockedQuantity?: number + remark?: string +} + +export interface Checkin { + checkinId: number + checkinCode: string + checkinDate: string + status: string + businessType: string + orderId?: number + orderCode?: string + supplierId?: number + supplierName?: string + warehouseId?: number + warehouseName?: string + totalQuantity?: number + stockedQuantity?: number + remark?: string + operatorName?: string + approverName?: string + approveDate?: string + lines: CheckinLine[] + createTime?: string +} + +export interface CheckinQuery { + trackCode?: string + checkinCode?: string + supplierName?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface CheckinListResponse { + rows: Checkin[] + total: number +} + +// 获取采购到货单列表 +export function getCheckinList(params: CheckinQuery): Promise { + return request.get('/erp/po/checkin/list', { params }).then((res: any) => { + return { + rows: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取采购到货单详情 +export function getCheckinDetail(checkinId: number): Promise { + return request.get(`/erp/po/checkin/${checkinId}`).then((res: any) => { + return res.data + }) +} + +// 新增采购到货单 +export function createCheckin(data: Partial): Promise { + return request.post('/erp/po/checkin', data) +} + +// 更新采购到货单 +export function updateCheckin(data: Partial): Promise { + return request.put('/erp/po/checkin', data) +} + +// 删除采购到货单 +export function deleteCheckin(checkinIds: string): Promise { + return request.delete(`/erp/po/checkin/${checkinIds}`) +} + +// 注意:采购到货单审核接口后端暂未实现 +// export function approveCheckin(checkinId: number): Promise { +// return request.post(`/erp/po/checkin/audit/${checkinId}`) +// } diff --git a/erp-frontend-vue/src/api/contract.ts b/erp-frontend-vue/src/api/contract.ts new file mode 100644 index 0000000..f81e706 --- /dev/null +++ b/erp-frontend-vue/src/api/contract.ts @@ -0,0 +1,63 @@ +import request from './request' + +export interface Contract { + contractId?: number + contractCode?: string + contractName?: string + clientId?: number + clientCode?: string + clientName?: string + contractDate?: string + startDate?: string + endDate?: string + status?: string + totalAmount?: number + remark?: string + createTime?: string + tenantId?: string +} + +export interface ContractQuery { + contractCode?: string + clientName?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface ContractListResponse { + list: Contract[] + total: number +} + +// 获取销售合同列表 +export function getContractList(params: ContractQuery): Promise { + return request.get('/sl/contract/list', { params }).then((res: any) => { + return { + list: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取销售合同详情 +export function getContractDetail(id: number): Promise { + return request.get(`/sl/contract/${id}`).then((res: any) => { + return res.data + }) +} + +// 新增销售合同 +export function createContract(data: Partial): Promise { + return request.post('/sl/contract', data) +} + +// 更新销售合同 +export function updateContract(id: number, data: Partial): Promise { + return request.put('/sl/contract', { ...data, contractId: id }) +} + +// 删除销售合同 +export function deleteContract(id: number): Promise { + return request.delete(`/sl/contract/${id}`) +} diff --git a/erp-frontend-vue/src/api/customer.ts b/erp-frontend-vue/src/api/customer.ts new file mode 100644 index 0000000..e93de8e --- /dev/null +++ b/erp-frontend-vue/src/api/customer.ts @@ -0,0 +1,111 @@ +import request from './request' + +/** 客户档案 - 与 ERP 数据模型 SlClient 对齐 */ +export interface Customer { + clientId?: number + clientCode?: string + clientName?: string + clientNick?: string + /** 兼容表单选择:id = clientId */ + id?: number + /** 兼容表单选择:code = clientCode */ + code?: string + /** 兼容表单选择:name = clientName */ + name?: string + /** 兼容表单选择:contact = contact1 */ + contact?: string + /** 兼容表单选择:phone = contact1Tel */ + phone?: string + clientEn?: string + clientDes?: string + clientLogo?: string + clientType?: string + clientLevel?: string + clientSource?: string + clientIndustry?: string + address?: string + website?: string + email?: string + tel?: string + contact1?: string + contact1Tel?: string + contact2?: string + contact2Tel?: string + creditCode?: string + bankName?: string + bankAccount?: string + taxNo?: string + invoiceAddress?: string + enableFlag?: 'Y' | 'N' + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +export interface CustomerQuery { + clientCode?: string + clientName?: string + clientNick?: string + /** 兼容:映射为 clientName */ + name?: string + enableFlag?: 'Y' | 'N' + pageNum?: number + pageSize?: number +} + +export interface CustomerListResponse { + list: Customer[] + total: number +} + +const BASE = '/erp/sl/client' + +function mapRow(r: any): Customer { + return { + ...r, + id: r.clientId ?? r.id, + code: r.clientCode ?? r.code, + name: r.clientName ?? r.name, + contact: r.contact1 ?? r.contact, + phone: r.contact1Tel ?? r.phone + } +} + +/** 获取客户列表(分页) */ +export function getCustomerList(params: CustomerQuery): Promise { + const { name, ...rest } = params + const req = { ...rest, clientName: rest.clientName ?? name } as Record + return request.get(`${BASE}/list`, { params: req }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? res.list ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { list: rows.map(mapRow), total } + }) +} + +/** 获取客户详情 */ +export function getCustomerDetail(clientId: number): Promise { + return request.get(`${BASE}/${clientId}`).then((res: any) => mapRow(res.data ?? res)) +} + +/** 新增客户 */ +export function createCustomer(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 更新客户 */ +export function updateCustomer(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除客户(支持单个或批量,逗号分隔 ID) */ +export function deleteCustomer(clientIds: number | number[]): Promise { + const ids = Array.isArray(clientIds) ? clientIds.join(',') : String(clientIds) + return request.delete(`${BASE}/${ids}`) +} + +/** 更新客户启用状态 */ +export function updateCustomerStatus(clientId: number, enableFlag: 'Y' | 'N'): Promise { + return request.put(BASE, { clientId, enableFlag }) +} diff --git a/erp-frontend-vue/src/api/deliver.ts b/erp-frontend-vue/src/api/deliver.ts new file mode 100644 index 0000000..44237f0 --- /dev/null +++ b/erp-frontend-vue/src/api/deliver.ts @@ -0,0 +1,108 @@ +import request from './request' + +export interface DeliverLine { + lineId?: number + id?: number + deliverId?: number + deliverCode?: string + orderLineId?: number + itemId?: number + itemCode?: string + materialCode?: string + itemName?: string + materialName?: string + specification?: string + spec?: string + unitOfMeasure?: string + unit?: string + orderQty?: number + deliverQty: number + remark?: string +} + +export interface Deliver { + deliverId?: number + id?: number + deliverCode?: string + code?: string + clientId?: number + customerId?: number + clientCode?: string + customerCode?: string + clientName?: string + customerName?: string + deliverDate: string + status: string + totalQty?: number + deliveryAddress?: string + contact?: string + phone?: string + remark?: string + lines?: DeliverLine[] + createTime?: string +} + +export interface DeliverQuery { + code?: string + customerName?: string + status?: string + page?: number + pageSize?: number +} + +export interface DeliverListResponse { + list: Deliver[] + total: number +} + +// 获取发货单列表 +export function getDeliverList(params: DeliverQuery): Promise { + return request.get('/erp/sl/deliver/list', { params }).then((res: any) => { + return { + list: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取发货单详情 +export function getDeliverDetail(id: number): Promise { + return request.get(`/erp/sl/deliver/${id}`).then((res: any) => { + return res.data + }) +} + +// 新增发货单 +export function createDeliver(data: Partial): Promise { + return request.post('/erp/sl/deliver', data) +} + +// 更新发货单 +export function updateDeliver(id: number, data: Partial): Promise { + return request.put('/erp/sl/deliver', { ...data, deliverId: id }) +} + +// 删除发货单 +export function deleteDeliver(id: number): Promise { + return request.delete(`/erp/sl/deliver/${id}`) +} + +// 审核发货单 +export function auditDeliver(id: number): Promise { + return request.post(`/erp/sl/deliver/audit/${id}`) +} + +// 反审核发货单 +export function unauditDeliver(id: number): Promise { + return request.post(`/erp/sl/deliver/unaudit/${id}`) +} + +// 引入销售订单 +export function getIntroduceOrderList(params: any): Promise { + return request.get('/erp/sl/deliver/introduce', { params }).then((res: any) => { + return { + list: res.rows || [], + total: res.total || 0 + } + }) +} diff --git a/erp-frontend-vue/src/api/invoice.ts b/erp-frontend-vue/src/api/invoice.ts new file mode 100644 index 0000000..8218eb4 --- /dev/null +++ b/erp-frontend-vue/src/api/invoice.ts @@ -0,0 +1,77 @@ +import request from './request' + +export interface Invoice { + invoiceId?: number + id?: number + invoiceCode?: string + code?: string + clientId?: number + customerId?: number + clientCode?: string + customerCode?: string + clientName?: string + customerName?: string + invoiceDate: string + invoiceType?: string + status: string + totalAmount?: number + taxAmount?: number + invoiceNo?: string + remark?: string + createTime?: string +} + +export interface InvoiceQuery { + code?: string + customerName?: string + status?: string + page?: number + pageSize?: number +} + +export interface InvoiceListResponse { + list: Invoice[] + total: number +} + +// 获取销售发票列表 +export function getInvoiceList(params: InvoiceQuery): Promise { + return request.get('/erp/sl/invoice/list', { params }).then((res: any) => { + return { + list: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取销售发票详情 +export function getInvoiceDetail(id: number): Promise { + return request.get(`/erp/sl/invoice/${id}`).then((res: any) => { + return res.data + }) +} + +// 新增销售发票 +export function createInvoice(data: Partial): Promise { + return request.post('/erp/sl/invoice', data) +} + +// 更新销售发票 +export function updateInvoice(id: number, data: Partial): Promise { + return request.put('/erp/sl/invoice', { ...data, invoiceId: id }) +} + +// 删除销售发票 +export function deleteInvoice(id: number): Promise { + return request.delete(`/erp/sl/invoice/${id}`) +} + +// 审核销售发票 +export function auditInvoice(id: number): Promise { + return request.post(`/erp/sl/invoice/audit/${id}`) +} + +// 反审核销售发票 +export function unauditInvoice(id: number): Promise { + return request.post(`/erp/sl/invoice/unaudit/${id}`) +} diff --git a/erp-frontend-vue/src/api/masterdata/bom.ts b/erp-frontend-vue/src/api/masterdata/bom.ts new file mode 100644 index 0000000..2e62fca --- /dev/null +++ b/erp-frontend-vue/src/api/masterdata/bom.ts @@ -0,0 +1,201 @@ +import request from '../request' + +/** BOM 表头(md_bom) */ +export interface BomHeader { + bomId?: number + bomCode?: string + bomName?: string + version?: string + versionDesc?: string + status?: string + itemId?: number + itemCode?: string + itemName?: string + itemSpec?: string + unitName?: string + baseQty?: number + enableFlag?: string + bomItemId?: number + bomItemCode?: string + bomItemName?: string + bomItemSpec?: string + unitOfMeasure?: string + itemOrProduct?: string + quantity?: number + lineNo?: number + lossRate?: number + supplyType?: string + tenantId?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +/** BOM 明细(md_product_bom) */ +export interface BomLine { + bomId?: number // 明细行ID(现有表主键) + bomCode?: string + bomName?: string + version?: string + versionDesc?: string + status?: string + itemId?: number // 母件物料ID + itemCode?: string + itemName?: string + itemSpec?: string + bomItemId?: number + bomItemCode?: string + bomItemName?: string + bomItemSpec?: string + unitOfMeasure?: string + unitName?: string + itemOrProduct?: string + quantity?: number + baseQty?: number + lineNo?: number + lossRate?: number + supplyType?: string + enableFlag?: string + tenantId?: string + delFlag?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + /** 扩展字段(后端可忽略/不落库,页面需要透传) */ + attr1?: string + attr2?: string + // 详情页表格里用到的前端临时字段(后端可忽略) + planRoute?: string + usageType?: string + drawingNo?: string +} + +/** BOM 表头查询参数 */ +export interface BomHeaderQuery { + bomCode?: string + bomName?: string + itemId?: number + itemCode?: string + itemName?: string + itemOrProduct?: string + itemTypeId?: number + status?: string + enableFlag?: string + pageNum?: number + pageSize?: number +} + +/** BOM 表头列表响应 */ +export interface BomHeaderListResponse { + rows: BomHeader[] + total: number +} + +/** BOM 明细查询参数 */ +export interface BomLineQuery { + bomCode?: string + itemId?: number + bomItemCode?: string + bomItemName?: string + status?: string + enableFlag?: string + pageNum?: number + pageSize?: number +} + +/** BOM 明细列表响应 */ +export interface BomLineListResponse { + rows: BomLine[] + total: number +} + +const BOM_HEADER_BASE = '/mes/md/bom' +const BOM_LINE_BASE = '/mes/md/bom/line' + +/** 查询BOM表头列表 */ +export function listBomHeader(query?: BomHeaderQuery): Promise { + return request.get(`${BOM_HEADER_BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询BOM表头详情 */ +export function getBomHeader(bomId: number): Promise<{ data: BomHeader }> { + return request.get(`${BOM_HEADER_BASE}/${bomId}`) +} + +/** 新增BOM表头 */ +export function addBomHeader(data: Partial): Promise<{ data: BomHeader }> { + return request.post(BOM_HEADER_BASE, data) +} + +/** 修改BOM表头 */ +export function updateBomHeader(data: Partial): Promise<{ data: BomHeader }> { + return request.put(BOM_HEADER_BASE, data) +} + +/** 删除BOM表头 */ +export function delBomHeader(bomId: number | number[]): Promise { + const ids = Array.isArray(bomId) ? bomId.join(',') : String(bomId) + return request.delete(`${BOM_HEADER_BASE}/${ids}`) +} + +/** 查询BOM明细列表 */ +export function listBomLine(query?: BomLineQuery): Promise { + return request.get(`${BOM_LINE_BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询BOM明细详情 */ +export function getBomLine(lineId: number): Promise<{ data: BomLine }> { + return request.get(`${BOM_LINE_BASE}/${lineId}`) +} + +/** 新增BOM明细 */ +export function addBomLine(data: Partial): Promise<{ data: BomLine }> { + return request.post(BOM_LINE_BASE, data) +} + +/** 修改BOM明细 */ +export function updateBomLine(data: Partial): Promise<{ data: BomLine }> { + return request.put(BOM_LINE_BASE, data) +} + +/** 删除BOM明细 */ +export function delBomLine(lineId: number | number[]): Promise { + const ids = Array.isArray(lineId) ? lineId.join(',') : String(lineId) + return request.delete(`${BOM_LINE_BASE}/${ids}`) +} + +// ----------------------------- +// 兼容旧命名(当前BOM页面仍在使用) +// ----------------------------- +export type ProductBom = BomHeader +export type ProductBomQuery = BomHeaderQuery +export type ProductBomListResponse = BomHeaderListResponse +export const listProductBom = listBomHeader +export const getProductBom = getBomHeader +export const addProductBom = addBomHeader +export const updateProductBom = updateBomHeader +export const delProductBom = delBomHeader + +/** BOM状态选项 */ +export const bomStatusOptions = [ + { value: 'DRAFT', label: '草稿' }, + { value: 'APPROVED', label: '已审核' }, + { value: 'OBSOLETE', label: '已废弃' } +] + +/** 供应方式选项 */ +export const supplyTypeOptions = [ + { value: 'PURCHASE', label: '采购' }, + { value: 'PRODUCE', label: '自制' }, + { value: 'OUTSOURCE', label: '委外' } +] diff --git a/erp-frontend-vue/src/api/masterdata/item.ts b/erp-frontend-vue/src/api/masterdata/item.ts new file mode 100644 index 0000000..626e2af --- /dev/null +++ b/erp-frontend-vue/src/api/masterdata/item.ts @@ -0,0 +1,70 @@ +import request from '../request' + +export interface MdItem { + itemId?: number + itemCode?: string + itemName?: string + specification?: string + unitOfMeasure?: string + unitName?: string + itemTypeId?: number + itemTypeName?: string + itemOrProduct?: string + enableFlag?: string + safeStockFlag?: string + minStock?: number + maxStock?: number + batchFlag?: string + highValue?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +export interface MdItemQuery { + itemCode?: string + itemName?: string + itemTypeId?: number + itemOrProduct?: string + enableFlag?: string + pageNum?: number + pageSize?: number +} + +export interface MdItemListResponse { + rows: MdItem[] + total: number +} + +const BASE = '/mes/md/mditem' + +/** 查询物料列表 */ +export function listMdItem(query?: MdItemQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询物料详细 */ +export function getMdItem(itemId: number): Promise<{ data: MdItem }> { + return request.get(`${BASE}/${itemId}`) +} + +/** 新增物料 */ +export function addMdItem(data: Partial): Promise<{ data: MdItem }> { + return request.post(BASE, data) +} + +/** 修改物料 */ +export function updateMdItem(data: Partial): Promise<{ data: MdItem }> { + return request.put(BASE, data) +} + +/** 删除物料 */ +export function delMdItem(itemId: number | number[]): Promise { + const ids = Array.isArray(itemId) ? itemId.join(',') : String(itemId) + return request.delete(`${BASE}/${ids}`) +} diff --git a/erp-frontend-vue/src/api/masterdata/itemtype.ts b/erp-frontend-vue/src/api/masterdata/itemtype.ts new file mode 100644 index 0000000..64509ec --- /dev/null +++ b/erp-frontend-vue/src/api/masterdata/itemtype.ts @@ -0,0 +1,79 @@ +import request from '../request' + +export interface ItemType { + itemTypeId?: number + parentTypeId?: number + itemTypeCode?: string + itemTypeName?: string + orderNum?: number + enableFlag?: string + itemOrProduct?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + children?: ItemType[] +} + +export interface ItemTypeQuery { + itemTypeCode?: string + itemTypeName?: string + enableFlag?: string +} + +const BASE = '/mes/md/itemtype' + +/** 查询物料分类列表 */ +export function listItemType(query?: ItemTypeQuery): Promise<{ data: ItemType[] }> { + return request.get(`${BASE}/list`, { params: query }) +} + +/** 查询物料分类下拉树结构 */ +export function getItemTypeTreeselect(): Promise<{ data: any[] }> { + return request.get(`${BASE}/treeselect`) +} + +/** 查询物料分类详细 */ +export function getItemType(itemTypeId: number): Promise<{ data: ItemType }> { + return request.get(`${BASE}/${itemTypeId}`) +} + +/** 新增物料分类 */ +export function addItemType(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改物料分类 */ +export function updateItemType(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除物料分类 */ +export function delItemType(itemTypeId: number): Promise { + return request.delete(`${BASE}/${itemTypeId}`) +} + +/** 构建分类树 */ +export function handleTree(data: ItemType[], idField = 'itemTypeId', parentField = 'parentTypeId'): ItemType[] { + const map = new Map() + const result: ItemType[] = [] + + data.forEach(item => { + map.set(item[idField as keyof ItemType] as number, { ...item, children: [] }) + }) + + data.forEach(item => { + const current = map.get(item[idField as keyof ItemType] as number)! + const parentId = item[parentField as keyof ItemType] as number + if (parentId && map.has(parentId)) { + const parent = map.get(parentId)! + parent.children = parent.children || [] + parent.children.push(current) + } else { + result.push(current) + } + }) + + return result +} diff --git a/erp-frontend-vue/src/api/masterdata/unit.ts b/erp-frontend-vue/src/api/masterdata/unit.ts new file mode 100644 index 0000000..a0c2163 --- /dev/null +++ b/erp-frontend-vue/src/api/masterdata/unit.ts @@ -0,0 +1,65 @@ +import request from '../request' + +export interface UnitMeasure { + measureId?: number + measureCode?: string + measureName?: string + primaryFlag?: string + primaryId?: number + changeRate?: number + enableFlag?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +export interface UnitMeasureQuery { + measureCode?: string + measureName?: string + enableFlag?: string + pageNum?: number + pageSize?: number +} + +export interface UnitMeasureListResponse { + rows: UnitMeasure[] + total: number +} + +const BASE = '/mes/md/unitmeasure' + +/** 查询计量单位列表 */ +export function listUnitMeasure(query?: UnitMeasureQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询所有计量单位(不分页) */ +export function listAllUnitMeasure(): Promise<{ data: UnitMeasure[] }> { + return request.get(`${BASE}/selectall`) +} + +/** 查询计量单位详细 */ +export function getUnitMeasure(measureId: number): Promise<{ data: UnitMeasure }> { + return request.get(`${BASE}/${measureId}`) +} + +/** 新增计量单位 */ +export function addUnitMeasure(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改计量单位 */ +export function updateUnitMeasure(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除计量单位 */ +export function delUnitMeasure(measureId: number | number[]): Promise { + const ids = Array.isArray(measureId) ? measureId.join(',') : String(measureId) + return request.delete(`${BASE}/${ids}`) +} diff --git a/erp-frontend-vue/src/api/masterdata/workshop.ts b/erp-frontend-vue/src/api/masterdata/workshop.ts new file mode 100644 index 0000000..2eec971 --- /dev/null +++ b/erp-frontend-vue/src/api/masterdata/workshop.ts @@ -0,0 +1,64 @@ +import request from '../request' + +export interface Workshop { + workshopId?: number + workshopCode?: string + workshopName?: string + area?: string + charge?: string + enableFlag?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +export interface WorkshopQuery { + workshopCode?: string + workshopName?: string + enableFlag?: string + pageNum?: number + pageSize?: number +} + +export interface WorkshopListResponse { + rows: Workshop[] + total: number +} + +const BASE = '/mes/md/workshop' + +/** 查询车间列表 */ +export function listWorkshop(query?: WorkshopQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询所有车间(不分页) */ +export function listAllWorkshop(): Promise<{ data: Workshop[] }> { + return request.get(`${BASE}/listAll`) +} + +/** 查询车间详细 */ +export function getWorkshop(workshopId: number): Promise<{ data: Workshop }> { + return request.get(`${BASE}/${workshopId}`) +} + +/** 新增车间 */ +export function addWorkshop(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改车间 */ +export function updateWorkshop(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除车间 */ +export function delWorkshop(workshopId: number | number[]): Promise { + const ids = Array.isArray(workshopId) ? workshopId.join(',') : String(workshopId) + return request.delete(`${BASE}/${ids}`) +} diff --git a/erp-frontend-vue/src/api/masterdata/workstation.ts b/erp-frontend-vue/src/api/masterdata/workstation.ts new file mode 100644 index 0000000..2bf71c4 --- /dev/null +++ b/erp-frontend-vue/src/api/masterdata/workstation.ts @@ -0,0 +1,68 @@ +import request from '../request' + +export interface Workstation { + workstationId?: number + workstationCode?: string + workstationName?: string + workstationAddress?: string + workshopId?: number + workshopName?: string + processId?: number + processName?: string + enableFlag?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +export interface WorkstationQuery { + workstationCode?: string + workstationName?: string + workshopId?: number + enableFlag?: string + pageNum?: number + pageSize?: number +} + +export interface WorkstationListResponse { + rows: Workstation[] + total: number +} + +const BASE = '/mes/md/workstation' + +/** 查询工作站列表 */ +export function listWorkstation(query?: WorkstationQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询所有工作站(不分页) */ +export function listAllWorkstation(): Promise<{ data: Workstation[] }> { + return request.get(`${BASE}/listAll`) +} + +/** 查询工作站详细 */ +export function getWorkstation(workstationId: number): Promise<{ data: Workstation }> { + return request.get(`${BASE}/${workstationId}`) +} + +/** 新增工作站 */ +export function addWorkstation(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改工作站 */ +export function updateWorkstation(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除工作站 */ +export function delWorkstation(workstationId: number | number[]): Promise { + const ids = Array.isArray(workstationId) ? workstationId.join(',') : String(workstationId) + return request.delete(`${BASE}/${ids}`) +} diff --git a/erp-frontend-vue/src/api/material.ts b/erp-frontend-vue/src/api/material.ts new file mode 100644 index 0000000..ef625c8 --- /dev/null +++ b/erp-frontend-vue/src/api/material.ts @@ -0,0 +1,78 @@ +import { listMdItem, getMdItem, type MdItem } from './masterdata/item' + +export interface Material { + id: number + code: string + name: string + spec?: string + unit: string + category?: string + price?: number + status: number +} + +export interface MaterialQuery { + code?: string + name?: string + category?: string + itemOrProduct?: string + page?: number + pageSize?: number +} + +export interface MaterialListResponse { + list: Material[] + total: number +} + +/** 将后端MdItem转换为前端Material */ +function convertMdItemToMaterial(item: MdItem): Material { + return { + id: item.itemId || 0, + code: item.itemCode || '', + name: item.itemName || '', + spec: item.specification, + unit: item.unitOfMeasure || item.unitName || '', + category: item.itemTypeName, + price: 0, // 物料主数据中没有价格,价格在订单中设置 + status: item.enableFlag === 'Y' ? 1 : 0 + } +} + +/** 获取物料列表 */ +export async function getMaterialList(params: MaterialQuery): Promise { + try { + const query: any = { + itemCode: params.code, + itemName: params.name, + pageNum: params.page || 1, + pageSize: params.pageSize || 100 + } + if (params.itemOrProduct) { + query.itemOrProduct = params.itemOrProduct + } + + const res = await listMdItem(query) + // 返回所有物料,让用户自行选择 + const list = res.rows.map(convertMdItemToMaterial) + + return { + list, + total: res.total + } + } catch (error) { + console.error('获取物料列表失败:', error) + return { list: [], total: 0 } + } +} + +/** 获取物料详情 */ +export async function getMaterialDetail(id: number): Promise { + try { + const res = await getMdItem(id) + return convertMdItemToMaterial(res.data) + } catch (error) { + console.error('获取物料详情失败:', error) + throw error + } +} diff --git a/erp-frontend-vue/src/api/mbom.ts b/erp-frontend-vue/src/api/mbom.ts new file mode 100644 index 0000000..166db31 --- /dev/null +++ b/erp-frontend-vue/src/api/mbom.ts @@ -0,0 +1,142 @@ +import request from './request' + +export interface Mbom { + mbomId: number + mbomCode: string + mbomDate: string + status: string + businessType: string + issueStatus?: string + planId: number + planCode: string + salesOrderId?: number + salesOrderCode?: string + deliveryDate?: string + itemId: number + itemCode: string + itemName: string + unitName?: string + supplyType?: string + quantity: number + workshopId?: number + workshopName?: string + issueDate?: string + approverName?: string + approveDate?: string + remark?: string + createTime?: string +} + +/** 物料清单明细行 */ +export interface MbomLine { + lineId: number + mbomId: number + mbomCode?: string + lineNo?: number + bomLevel?: number + itemId: number + itemCode: string + itemName: string + specification?: string + unitName?: string + baseQty?: number + quantity: number + lossRate?: number + supplyType?: string + pickType?: string + remark?: string +} + +/** 物料清单详情(表头 + 明细行) */ +export interface MbomDetail extends Mbom { + lines?: MbomLine[] +} + +export interface MbomQuery { + salesOrderCode?: string + mbomCode?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string + status?: string + supplyType?: string + pageNum?: number + pageSize?: number +} + +export interface MbomListResponse { + rows: Mbom[] + total: number +} + +// 获取物料清单列表 +export function getMbomList(params: MbomQuery): Promise { + return request.get('/erp/mp/mbom/list', { params }).then((res: any) => { + return { + rows: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取物料清单详情(含明细行) +export function getMbomDetail(mbomId: number): Promise { + return request.get(`/erp/mp/mbom/${mbomId}`).then((res: any) => { + const data = res.data ?? res + return data as MbomDetail + }) +} + +// 获取物料清单明细列表 +export function getMbomLines(mbomId: number): Promise { + return request.get(`/erp/mp/mbomline/mbom/${mbomId}`).then((res: any) => { + const rows = res.rows ?? res.data ?? [] + return rows as MbomLine[] + }) +} + +// 更新物料清单明细行 +export function updateMbomLine(data: Partial): Promise { + return request.put('/erp/mp/mbomline', data) +} + +// 新增物料清单 +export function createMbom(data: Partial): Promise { + return request.post('/erp/mp/mbom', data) +} + +// 更新物料清单 +export function updateMbom(data: Partial): Promise { + return request.put('/erp/mp/mbom', data) +} + +// 删除物料清单 +export function deleteMbom(mbomIds: string): Promise { + return request.delete(`/erp/mp/mbom/${mbomIds}`) +} + +// 审核物料清单 +export function approveMbom(mbomId: number): Promise { + return request.put(`/erp/mp/mbom/approve/${mbomId}`) +} + +// 反审核物料清单 +export function unapproveMbom(mbomId: number): Promise { + return request.put(`/erp/mp/mbom/unapprove/${mbomId}`) +} + +// 下发车间 +export function issueToWorkshop(mbomId: number, workshopId: number, workshopName: string): Promise { + return request.put(`/erp/mp/mbom/issue/${mbomId}/${workshopId}/${workshopName}`) +} + +// 撤销下发车间 +export function revokeIssue(mbomId: number): Promise { + return request.put(`/erp/mp/mbom/revoke-issue/${mbomId}`) +} + +// BOM运算(从生产计划) +export function calcBom(planId: number): Promise { + return request.put(`/erp/mp/plan/bom-calculate/${planId}`) +} diff --git a/erp-frontend-vue/src/api/parts.ts b/erp-frontend-vue/src/api/parts.ts new file mode 100644 index 0000000..bef85ab --- /dev/null +++ b/erp-frontend-vue/src/api/parts.ts @@ -0,0 +1,72 @@ +import request from './request' + +export interface PartsOrder { + partsId: number + partsCode: string + partsDate: string + status: string + itemId: number + itemCode: string + itemName: string + specification?: string + drawingNo?: string + unitName?: string + quantity: number + planQty?: number + completedQty?: number + remark?: string + createTime?: string +} + +export interface PartsQuery { + partsCode?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface PartsListResponse { + rows: PartsOrder[] + total: number +} + +// 获取零部件订单列表 +export function getPartsOrderList(params: PartsQuery): Promise { + return request.get('/erp/mp/parts/list', { params }).then((res: any) => { + return { + rows: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取零部件订单详情 +export function getPartsOrderDetail(partsId: number): Promise { + return request.get(`/erp/mp/parts/${partsId}`).then((res: any) => { + return res.data + }) +} + +// 新增零部件订单 +export function createPartsOrder(data: Partial): Promise { + return request.post('/erp/mp/parts', data) +} + +// 更新零部件订单 +export function updatePartsOrder(data: Partial): Promise { + return request.put('/erp/mp/parts', data) +} + +// 删除零部件订单 +export function deletePartsOrder(partsIds: string): Promise { + return request.delete(`/erp/mp/parts/${partsIds}`) +} + +// 审核零部件订单 +export function approvePartsOrder(partsId: number): Promise { + return request.put(`/erp/mp/parts/approve/${partsId}`) +} diff --git a/erp-frontend-vue/src/api/productionPlan.ts b/erp-frontend-vue/src/api/productionPlan.ts new file mode 100644 index 0000000..3c78367 --- /dev/null +++ b/erp-frontend-vue/src/api/productionPlan.ts @@ -0,0 +1,357 @@ +import request from './request' + +// ============ 类型定义 ============ + +/** 生产计划明细行 */ +export interface PlanLine { + lineId?: number + planId?: number + planCode?: string + lineNo?: number + salesLineId?: number + trackCode?: string + itemId?: number + itemCode?: string + itemName?: string + specification?: string + unitId?: number + unitName?: string + quantity?: number + qualityReq?: string + remark?: string + delFlag?: string +} + +/** 生产计划单主表 */ +export interface ProductionPlan { + planId?: number + planCode?: string + planDate?: string + status?: string + businessStatus?: string + businessType?: string + workType?: number + salesOrderId?: number + salesOrderCode?: string + salesUserId?: number + salesUserName?: string + deliveryDate?: string + bomId?: number + /** EBOM单号(选择BOM后显示在表头订单BOM) */ + bomCode?: string + bomVersion?: string + bomDesc?: string + totalQuantity?: number + remark?: string + operatorId?: number + operatorName?: string + approverId?: number + approverName?: string + approveDate?: string + delFlag?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + lines?: PlanLine[] + slaveList?: PlanLine[] + /** 部门名称(单据视图) */ + deptName?: string + /** 操作员/业务人员(单据视图表头) */ + operatorName?: string + /** 审核日期 */ + approveDate?: string + /** 物料清单(BOM运算结果,详情页只读) */ + mbomList?: MbomLine[] + /** 补料清单(详情页只读) */ + supplementList?: SupplementLine[] +} + +/** 明细视图数据(展开物料维度) */ +export interface PlanDetailView { + lineId: number + planId: number + planCode: string + planDate: string + status: string + workType: number + salesOrderCode?: string + salesUserName?: string + deliveryDate?: string + trackCode?: string + itemId: number + itemCode: string + itemName: string + specification?: string + unitName?: string + quantity: number + /** 物料清单状态:BOM运算 / BOM运算,BOM补料 / 空 */ + mbomStatus?: string +} + +/** 物料清单行(BOM运算结果) */ +export interface MbomLine { + mbomId?: number + mbomCode?: string + mbomStatus?: string + businessType?: string + issueStatus?: string + itemCode?: string + itemName?: string + unitName?: string + supplyType?: string + productionQty?: number + workshopId?: number + workshopName?: string + issueDate?: string +} + +/** 补料清单行 */ +export interface SupplementLine { + supCode?: string + supStatus?: string + businessType?: string + issueStatus?: string + itemCode?: string + itemName?: string + unitName?: string + supplyType?: string + productionQty?: number + supReason?: string + supDate?: string + remark?: string +} + +/** 查询参数 */ +export interface PlanQuery { + salesOrderCode?: string + planCode?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string + workType?: number + status?: string + businessStatus?: string + pageNum?: number + pageSize?: number +} + +/** 分页响应 */ +export interface PlanListResponse { + rows: ProductionPlan[] + total: number +} + +export interface PlanDetailListResponse { + rows: PlanDetailView[] + total: number +} + +/** 引入订单数据类型 */ +export interface ImportOrder { + orderId: number + orderCode: string + orderDate: string + userId?: number + userName?: string + salesmanName?: string + clientName?: string + deliveryDate?: string + deliveryStatus?: string + businessType?: string + remark?: string + lines?: ImportOrderLine[] +} + +/** 引入订单明细 */ +export interface ImportOrderLine { + lineId: number + orderId: number + itemId: number + itemCode: string + itemName: string + specification?: string + unitOfMeasure?: string + quantity: number + qualityReq?: string + remark?: string +} + +// ============ 状态映射 ============ + +export const PLAN_STATUS_MAP: Record = { + 'PREPARE': { label: '开立', type: 'info' }, + 'DRAFT': { label: '开立', type: 'info' }, + '开立': { label: '开立', type: 'info' }, + 'APPROVED': { label: '审核', type: 'success' }, + '审核': { label: '审核', type: 'success' }, + 'CLOSED': { label: '关闭', type: 'info' } +} + +export const BUSINESS_STATUS_OPTIONS = [ + { value: 'NORMAL', label: '正常' }, + { value: 'PAUSE', label: '暂停' }, + { value: 'CANCEL', label: '取消' } +] + +export const WORK_TYPE_OPTIONS = [ + { value: 0, label: '生产计划单' }, + { value: 1, label: '零部件计划单' }, + { value: 2, label: '零部件订单' } +] + +// ============ API 接口 ============ + +const BASE = '/erp/mp/plan' + +/** 获取生产计划单列表(单据视图) */ +export function getProductionPlanList(params: PlanQuery): Promise { + return request.get(`${BASE}/list`, { params }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { rows, total } + }) +} + +/** 获取生产计划单明细列表(明细视图,展开物料维度) */ +export function getProductionPlanDetailList(params: PlanQuery): Promise { + return request.get(`${BASE}/lineList`, { params }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { rows, total } + }) +} + +/** 获取生产计划单详情(含明细行) */ +export function getProductionPlanDetail(planId: number): Promise { + return request.get(`${BASE}/${planId}`).then((res: any) => { + const data = res.data ?? res + // 统一明细字段名 + if (data.slaveList && !data.lines) { + data.lines = data.slaveList + } + return data + }) +} + +/** 新增生产计划单 */ +export function createProductionPlan(data: Partial): Promise<{ planId: number }> { + const payload = { ...data } + if (payload.lines) { + payload.slaveList = payload.lines + delete payload.lines + } + return request.post(BASE, payload).then((res: any) => res.data ?? res) +} + +/** 更新生产计划单 */ +export function updateProductionPlan(data: Partial): Promise { + const payload = { ...data } + if (payload.lines) { + payload.slaveList = payload.lines + delete payload.lines + } + return request.put(BASE, payload) +} + +/** 删除生产计划单(支持批量) */ +export function deleteProductionPlan(planIds: number | number[] | string): Promise { + const ids = Array.isArray(planIds) ? planIds.join(',') : String(planIds) + return request.delete(`${BASE}/${ids}`) +} + +/** 审核生产计划单(支持批量) */ +export function approveProductionPlan(planIds: number | number[]): Promise { + const ids = Array.isArray(planIds) ? planIds.join(',') : String(planIds) + return request.put(`${BASE}/approve/${ids}`) +} + +/** 反审核生产计划单(支持批量) */ +export function unapproveProductionPlan(planIds: number | number[]): Promise { + const ids = Array.isArray(planIds) ? planIds.join(',') : String(planIds) + return request.put(`${BASE}/reject/${ids}`) +} + +/** 导出生产计划单 */ +export function exportProductionPlan(params: PlanQuery): Promise { + return request.get(`${BASE}/export`, { + params, + responseType: 'blob' + }) +} + +/** BOM运算 */ +export function bomCalculate(planId: number): Promise { + return request.post(`/erp/mp/mbom/calc`, { planId }) +} + +// ============ 引入订单相关 ============ + +/** 获取可引入的销售订单列表(已审核的订单) */ +export function getImportOrderList(params?: { + orderCode?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string +}): Promise { + return request.get('/erp/sl/order/list', { + params: { + ...params, + orderStatus: '审核', + pageNum: 1, + pageSize: 100 + } + }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + // 转换字段名 + return rows.map((row: any) => ({ + orderId: row.orderId, + orderCode: row.orderCode, + orderDate: row.orderDate, + userId: row.salesmanId, + userName: row.salesmanName, + salesmanName: row.salesmanName, + clientName: row.clientName, + deliveryDate: row.deliveryDate, + deliveryStatus: row.deliveryDate && new Date(row.deliveryDate) < new Date() ? '超期' : '预计', + businessType: '销售订单', + remark: row.remark + })) + }) +} + +/** 获取销售订单详情(用于引入订单明细) */ +export function getImportOrderDetail(orderId: number): Promise { + return request.get(`/erp/sl/order/${orderId}`).then((res: any) => { + const data = res.data ?? res + const lines = data.lines ?? data.slaveList ?? [] + const salesPersonName = data.salesmanName ?? data.salesUserName ?? '' + return { + orderId: data.orderId, + orderCode: data.orderCode, + orderDate: data.orderDate, + userId: data.salesmanId ?? data.salesUserId, + userName: salesPersonName, + salesmanName: salesPersonName, + clientName: data.clientName, + deliveryDate: data.deliveryDate, + deliveryStatus: data.deliveryDate && new Date(data.deliveryDate) < new Date() ? '超期' : '预计', + businessType: '销售订单', + remark: data.remark, + lines: lines.map((line: any) => ({ + lineId: line.lineId, + orderId: line.orderId, + itemId: line.itemId, + itemCode: line.itemCode, + itemName: line.itemName, + specification: line.specification, + unitOfMeasure: line.unitOfMeasure, + quantity: line.quantity, + qualityReq: line.qualityReq, + remark: line.remark + })) + } + }) +} diff --git a/erp-frontend-vue/src/api/purchaseInvoice.ts b/erp-frontend-vue/src/api/purchaseInvoice.ts new file mode 100644 index 0000000..95c821d --- /dev/null +++ b/erp-frontend-vue/src/api/purchaseInvoice.ts @@ -0,0 +1,70 @@ +import request from './request' + +export interface PurchaseInvoice { + invoiceId: number + invoiceCode: string + invoiceDate: string + status: string + financeStatus: string + businessType: string + supplierId: number + supplierName: string + invoiceNo?: string + invoiceAmount: number + taxRate?: number + taxAmount?: number + totalAmount?: number + remark?: string + operatorName?: string + approverName?: string + approveDate?: string + createTime?: string +} + +export interface PurchaseInvoiceQuery { + invoiceCode?: string + supplierName?: string + invoiceNo?: string + beginDate?: string + endDate?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface PurchaseInvoiceListResponse { + rows: PurchaseInvoice[] + total: number +} + +// 获取采购发票列表 +export function getPurchaseInvoiceList(params: PurchaseInvoiceQuery): Promise { + return request.get('/erp/po/invoice/list', { params }).then((res: any) => { + return { + rows: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取采购发票详情 +export function getPurchaseInvoiceDetail(invoiceId: number): Promise { + return request.get(`/erp/po/invoice/${invoiceId}`).then((res: any) => { + return res.data + }) +} + +// 新增采购发票 +export function createPurchaseInvoice(data: Partial): Promise { + return request.post('/erp/po/invoice', data) +} + +// 更新采购发票 +export function updatePurchaseInvoice(data: Partial): Promise { + return request.put('/erp/po/invoice', data) +} + +// 删除采购发票 +export function deletePurchaseInvoice(invoiceIds: string): Promise { + return request.delete(`/erp/po/invoice/${invoiceIds}`) +} diff --git a/erp-frontend-vue/src/api/purchaseNeed.ts b/erp-frontend-vue/src/api/purchaseNeed.ts new file mode 100644 index 0000000..f4eaab9 --- /dev/null +++ b/erp-frontend-vue/src/api/purchaseNeed.ts @@ -0,0 +1,60 @@ +import request from './request' + +export interface PurchaseNeedRow { + purchaseId: number + salesOrderCode: string + planCode: string + purchaseCode: string + itemCode: string + itemName: string + demandQty: number + availableQty: number + purchaseQty: number + orderedQty: number + unorderedQty: number // = purchaseQty - orderedQty (backend returns in totalQuantity) + purchaseDate: string + remark?: string +} + +export interface PurchaseNeedQuery { + salesOrderCode?: string + itemCode?: string + itemName?: string + businessType?: string + beginDate?: string + endDate?: string + pageNum?: number + pageSize?: number +} + +export interface PurchaseNeedListResponse { + rows: PurchaseNeedRow[] + total: number +} + +export interface PurchaseNeedSummary { + totalPurchaseQty: number + totalOrderedQty: number + totalUnorderedQty: number +} + +// 查询采购计划需求列表 +export function getPurchaseNeedList(params: PurchaseNeedQuery): Promise { + return request.get('/erp/mp/purchase/need/list', { params }).then((res: any) => { + const rows = (res.rows || []).map((r: any) => ({ + ...r, + unorderedQty: r.totalQuantity // backend puts unordered qty in totalQuantity field + })) + return { rows, total: res.total || 0 } + }) +} + +// 查询采购计划需求汇总 +export function getPurchaseNeedSummary(params: PurchaseNeedQuery): Promise { + return request.get('/erp/mp/purchase/need/summary', { params }).then((res: any) => res.data || {}) +} + +// 导出采购计划需求表 +export function exportPurchaseNeed(params: PurchaseNeedQuery): Promise { + return request.post('/erp/mp/purchase/need/export', params, { responseType: 'blob' }) +} diff --git a/erp-frontend-vue/src/api/purchaseOrder.ts b/erp-frontend-vue/src/api/purchaseOrder.ts new file mode 100644 index 0000000..8888d50 --- /dev/null +++ b/erp-frontend-vue/src/api/purchaseOrder.ts @@ -0,0 +1,237 @@ +import request from './request' + +export interface PurchaseOrderLine { + lineId: number + orderId: number + orderCode: string + lineNo: number + trackCode?: string + planCode?: string + itemId: number + itemCode: string + itemName: string + specification?: string + unitName?: string + needDate?: string + quantity: number + unitPrice?: number + amount?: number + arrivedQuantity?: number + remark?: string +} + +export interface PurchaseOrder { + orderId: number + orderCode: string + orderDate: string + status: string + businessStatus?: string + businessType: string + orderType: string + materialNeed: string + supplierId?: number + supplierCode?: string + supplierName?: string + deptId?: number + deptName?: string + userId?: number + userName?: string + deliveryDate?: string + contractNo?: string + contractFile?: string + totalQuantity?: number + totalAmount?: number + arrivedQuantity?: number + remark?: string + operatorId?: number + operatorName?: string + approverId?: number + approverName?: string + approveDate?: string + lines: PurchaseOrderLine[] + createTime?: string + // Detail-view join fields + trackCode?: string + planCode?: string + itemCode?: string + itemName?: string + needDate?: string + quantity?: number + lineArrivedQuantity?: number +} + +/** 供应商(采购订单用,数据来源:md_vendor) */ +export interface PoSupplier { + supplierId: number + supplierCode?: string + supplierName: string + supplierNick?: string + tel?: string + contact1?: string + contact1Tel?: string + enableFlag?: string +} + +/** 引入弹窗行(采购计划明细) */ +export interface LeadInRow { + purchaseId: number + purchaseCode: string + salesOrderCode?: string + salesUserName?: string + deliveryDate?: string + itemId: number + itemCode: string + itemName: string + specification?: string + unitName?: string + demandDate?: string + purchaseQty: number + orderedQty: number + unpurchasedQty?: number +} + +export interface PurchaseOrderQuery { + trackCode?: string + orderCode?: string + supplierName?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface PurchaseOrderListResponse { + rows: PurchaseOrder[] + total: number +} + +// 获取采购订单列表 +export function getPurchaseOrderList(params: PurchaseOrderQuery): Promise { + return request.get('/erp/po/order/list', { params }).then((res: any) => { + return { + rows: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取采购订单详情 +export function getPurchaseOrderDetail(orderId: number): Promise { + return request.get(`/erp/po/order/${orderId}`).then((res: any) => { + return res.data + }) +} + +// 新增采购订单 +export function createPurchaseOrder(data: Partial): Promise { + return request.post('/erp/po/order', data) +} + +// 更新采购订单 +export function updatePurchaseOrder(data: Partial): Promise { + return request.put('/erp/po/order', data) +} + +// 删除采购订单 +export function deletePurchaseOrder(orderIds: string): Promise { + return request.delete(`/erp/po/order/${orderIds}`) +} + +// 获取采购订单单据视图列表 +export function getPurchaseOrderDocList(params: PurchaseOrderQuery): Promise { + return request.get('/erp/po/order/docList', { params }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +// 获取采购订单汇总 +export function getPurchaseOrderSummary(params: PurchaseOrderQuery): Promise<{ totalOrderQty: number; totalArrivedQty: number }> { + return request.get('/erp/po/order/summary', { params }).then((res: any) => res.data || {}) +} + +// 生成采购订单编码(后端返回的编码在 data 中,msg 为「操作成功」) +export function genPurchaseOrderCode(): Promise { + return request.get('/erp/po/order/genCode').then((res: any) => res.data ?? res.msg ?? '') +} + +// 审核采购订单 +export function approvePurchaseOrder(orderIds: string): Promise { + return request.put(`/erp/po/order/approve/${orderIds}`) +} + +// 反审核采购订单 +export function unapprovePurchaseOrder(orderIds: string): Promise { + return request.put(`/erp/po/order/unapprove/${orderIds}`) +} + +// 导出采购订单 +export function exportPurchaseOrder(params: PurchaseOrderQuery): Promise { + return request.post('/erp/po/order/export', params, { responseType: 'blob' }) +} + +// ============ 供应商 API(使用 MOM md_vendor,表 erp_po_supplier 在 my_mes 中未建) ============ + +export function getPoSupplierList(params?: { supplierName?: string; contact1?: string; pageNum?: number; pageSize?: number }): Promise<{ rows: PoSupplier[]; total: number }> { + const { supplierName, contact1, pageNum = 1, pageSize = 200, ...rest } = params ?? {} + return request.get('/mes/md/vendor/list', { + params: { enableFlag: 'Y', pageNum, pageSize, vendorName: supplierName, contact1, ...rest } + }).then((res: any) => { + const rows = (res.rows ?? []).map((v: any) => ({ + supplierId: v.vendorId, + supplierCode: v.vendorCode, + supplierName: v.vendorName ?? '', + supplierNick: v.vendorNick, + tel: v.tel, + contact1: v.contact1, + contact1Tel: v.contact1Tel, + enableFlag: v.enableFlag + })) + return { rows, total: res.total ?? 0 } + }) +} + +// ============ 引入采购计划单明细 API ============ + +export function getLeadInList(params?: { salesOrderCode?: string; itemCode?: string; itemName?: string; businessType?: string; pageNum?: number; pageSize?: number }): Promise<{ rows: LeadInRow[]; total: number }> { + return request.get('/erp/po/order/leadIn', { params: { pageNum: 1, pageSize: 200, ...params } }).then((res: any) => { + const rows = (res.rows ?? []).map((r: any) => ({ + ...r, + unpurchasedQty: (r.purchaseQty ?? 0) - (r.orderedQty ?? 0) + })) + return { rows, total: res.total ?? 0 } + }) +} + +// ============ 状态映射 ============ + +export const STATUS_MAP: Record = { + PREPARE: { label: '开立', type: 'info' }, + DRAFT: { label: '开立', type: 'info' }, + APPROVED: { label: '审核', type: 'success' }, + CLOSED: { label: '关闭', type: '' }, + REJECTED: { label: '退回', type: 'warning' } +} + +export const BUSINESS_STATUS_MAP: Record = { + NORMAL: '正常', COMPLETED: '完成', PAUSE: '暂停', CANCEL: '取消' +} + +export const BUSINESS_TYPE_OPTIONS = [ + { value: '原材料', label: '原材料' }, + { value: '零部件', label: '零部件' }, + { value: '装配件', label: '装配件' }, + { value: '成品', label: '成品' }, + { value: '五金件', label: '五金件' }, + { value: '包装物', label: '包装物' } +] + +export const BUSINESS_STATUS_OPTIONS = [ + { value: 'NORMAL', label: '正常' }, + { value: 'COMPLETED', label: '完成' }, + { value: 'PAUSE', label: '暂停' }, + { value: 'CANCEL', label: '取消' } +] diff --git a/erp-frontend-vue/src/api/purchasePlan.ts b/erp-frontend-vue/src/api/purchasePlan.ts new file mode 100644 index 0000000..8720493 --- /dev/null +++ b/erp-frontend-vue/src/api/purchasePlan.ts @@ -0,0 +1,344 @@ +import request from './request' + +// ============ Type Definitions ============ + +/** Purchase plan line item */ +export interface PurchasePlanLine { + purchaseId?: number + purchaseCode?: string + lineNo?: number + itemId?: number + itemCode: string + itemName: string + specification?: string + unitName?: string + demandDate?: string + demandQty?: number + availableQty?: number + purchaseQty: number + orderedQty?: number + remark?: string +} + +/** Purchase plan flat row (detail view) */ +export interface PurchasePlan { + purchaseId: number + purchaseCode: string + purchaseDate: string + status: string + needType: number + businessType?: string + businessStatus?: string + planId?: number + planCode?: string + salesOrderId?: number + salesOrderCode?: string + salesUserName?: string + deliveryDate?: string + itemId?: number + itemCode?: string + itemName?: string + specification?: string + unitName?: string + totalQuantity?: number + demandQty?: number + availableQty?: number + purchaseQty: number + orderedQty?: number + remark?: string + deptId?: number + deptName?: string + operatorId?: number + operatorName?: string + operatorName2?: string + approverId?: number + approverName?: string + approveDate?: string + createTime?: string +} + +/** Purchase plan detail (header + lines from /detail/{id} endpoint) */ +export interface PurchasePlanDetail { + purchaseId?: number + purchaseCode: string + purchaseDate: string + status: string + businessType?: string + businessStatus?: string + needType: number + planId?: number + planCode?: string + salesOrderId?: number + salesOrderCode?: string + salesUserName?: string + deliveryDate?: string + headerItemCode?: string + headerItemName?: string + unitName?: string + totalQuantity?: number + deptId?: number + deptName?: string + operatorId?: number + operatorName?: string + operatorName2?: string + approverId?: number + approverName?: string + approveDate?: string + remark?: string + lines: PurchasePlanLine[] +} + +/** List query params */ +export interface PurchasePlanQuery { + salesOrderCode?: string + purchaseCode?: string + itemCode?: string + itemName?: string + needType?: number + status?: string + businessStatus?: string + 'params[beginDate]'?: string + 'params[endDate]'?: string + 'params[itemTypeId]'?: number | string + pageNum?: number + pageSize?: number +} + +/** Summary response */ +export interface PurchasePlanSummary { + totalPurchaseQty: number + totalOrderedQty: number +} + +/** Import dialog - production plan parent row */ +export interface ImportPlanRow { + planId: number + planCode: string + salesOrderCode?: string + salesUserName?: string + deliveryDate?: string + itemCode?: string + itemName?: string + unitName?: string + quantity?: number + children?: ImportPlanMaterial[] +} + +/** Import dialog - material detail child row */ +export interface ImportPlanMaterial { + mbomId?: number + /** Stable unique key for table row-key (mbomId or composite fallback) */ + materialKey?: string + planId?: number + planCode?: string + salesOrderCode?: string + salesUserName?: string + deliveryDate?: string + itemId?: number + itemCode: string + itemName: string + specification?: string + unitName?: string + demandQty: number + availableQty?: number + purchasedQty?: number + unpurchasedQty?: number + remark?: string +} + +// ============ Status Maps ============ + +export const STATUS_MAP: Record = { + 'PREPARE': { label: '开立', type: 'info' }, + 'DRAFT': { label: '开立', type: 'info' }, + 'APPROVED': { label: '审核', type: 'success' }, + 'REJECTED': { label: '退回', type: 'warning' } +} + +export const STATUS_OPTIONS = [ + { value: 'PREPARE', label: '开立' }, + { value: 'APPROVED', label: '审核' }, + { value: 'REJECTED', label: '退回' } +] + +export const BUSINESS_STATUS_OPTIONS = [ + { value: 'NORMAL', label: '正常' }, + { value: 'PAUSE', label: '暂停' }, + { value: 'CANCEL', label: '取消' }, + { value: 'COMPLETED', label: '完成' } +] + +export const BUSINESS_STATUS_MAP: Record = { + 'NORMAL': '正常', + 'PAUSE': '暂停', + 'CANCEL': '取消', + 'COMPLETED': '完成' +} + +export const BUSINESS_TYPE_OPTIONS = [ + { value: '原材料', label: '原材料' }, + { value: '零部件', label: '零部件' }, + { value: '装配件', label: '装配件' }, + { value: '成品', label: '成品' }, + { value: '五金件', label: '五金件' }, + { value: '包装物', label: '包装物' } +] + +export const NEED_TYPE_OPTIONS = [ + { value: 0, label: '订单用料' }, + { value: 1, label: '库存备料' } +] + +export function isEditableStatus(status: string): boolean { + return status === 'PREPARE' || status === 'DRAFT' || status === 'REJECTED' +} + +export function isApprovedStatus(status: string): boolean { + return status === 'APPROVED' +} + +// ============ API Functions ============ + +const BASE = '/erp/mp/purchase' + +export function getPurchasePlanList(params: PurchasePlanQuery) { + return request.get(`${BASE}/list`, { params }).then((res: any) => ({ + rows: (res.rows ?? []) as PurchasePlan[], + total: (res.total ?? 0) as number + })) +} + +export function getPurchasePlanDocList(params: PurchasePlanQuery) { + return request.get(`${BASE}/docList`, { params }).then((res: any) => ({ + rows: (res.rows ?? []) as PurchasePlan[], + total: (res.total ?? 0) as number + })) +} + +export function getPurchasePlanSummary(params: PurchasePlanQuery) { + return request.get(`${BASE}/summary`, { params }).then((res: any) => { + const d = res.data ?? res + return { totalPurchaseQty: d.totalPurchaseQty ?? 0, totalOrderedQty: d.totalOrderedQty ?? 0 } as PurchasePlanSummary + }) +} + +export function getPurchasePlanDetail(idOrCode: number | string) { + return request.get(`${BASE}/detail/${idOrCode}`).then((res: any) => { + const d = res.data ?? res + if (!d.lines) d.lines = d.slaveList ?? d.details ?? [] + return d as PurchasePlanDetail + }) +} + +export function getPurchasePlanById(purchaseId: number) { + return request.get(`${BASE}/${purchaseId}`).then((res: any) => (res.data ?? res) as PurchasePlan) +} + +export function createPurchasePlan(data: Partial) { + return request.post(BASE, data).then((res: any) => res.data ?? res) +} + +export function updatePurchasePlan(data: Partial) { + return request.put(BASE, data) +} + +export function deletePurchasePlan(purchaseIds: string) { + return request.delete(`${BASE}/${purchaseIds}`) +} + +export function deletePurchasePlanByCode(purchaseCode: string) { + return request.delete(`${BASE}/byCode/${purchaseCode}`) +} + +export function approvePurchasePlan(purchaseIds: string | number) { + return request.put(`${BASE}/approve/${purchaseIds}`) +} + +export function unapprovePurchasePlan(purchaseIds: string | number) { + return request.put(`${BASE}/unapprove/${purchaseIds}`) +} + +export function genPurchaseCode() { + return request.get(`${BASE}/genCode`).then((res: any) => (res.data ?? res.msg ?? '') as string) +} + +export function exportPurchasePlan(params: PurchasePlanQuery) { + return request.post(`${BASE}/export`, params, { responseType: 'blob' }) +} + +export function generateFromMbom(mbomId: number) { + return request.post(`${BASE}/generate/${mbomId}`) +} + +// ============ Import Dialog APIs ============ + +export function getImportPlanList(params?: { + salesOrderCode?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string +}) { + const q: any = { status: 'APPROVED', pageNum: 1, pageSize: 200 } + if (params?.salesOrderCode) q.salesOrderCode = params.salesOrderCode + if (params?.itemCode) q.itemCode = params.itemCode + if (params?.itemName) q.itemName = params.itemName + if (params?.beginDate) q['params[beginDate]'] = params.beginDate + if (params?.endDate) q['params[endDate]'] = params.endDate + + return request.get('/erp/mp/plan/list', { params: q }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + return rows.map((row: any) => ({ + planId: row.planId, + planCode: row.planCode, + salesOrderCode: row.salesOrderCode, + salesUserName: row.salesUserName, + deliveryDate: row.deliveryDate, + itemCode: row.itemCode, + itemName: row.itemName, + unitName: row.unitName, + quantity: row.totalQuantity ?? row.quantity, + children: [] + })) as ImportPlanRow[] + }) +} + +export function getImportPlanMaterials(planId: number) { + // 查询该生产计划下的MBOM明细行(子件物料),不再过滤supplyType以展示所有物料 + return request.get(`/erp/mp/mbomline/plan/${planId}`).then((res: any) => { + const rows = res.data ?? res.rows ?? [] + return rows.map((row: any, index: number) => { + const lineId = row.lineId ?? row.mbomId + const materialKey = + lineId != null ? String(lineId) : `plan_${planId}_${row.itemId ?? row.itemCode ?? index}_${index}` + const demandQty = row.quantity ?? 0 + const purchasedQty = row.purchasedQty ?? row.orderedQty ?? 0 + return { + mbomId: lineId, + materialKey, + planId, + planCode: row.planCode ?? row.mbomCode, + itemId: row.itemId, + itemCode: row.itemCode, + itemName: row.itemName, + specification: row.specification, + unitName: row.unitName, + demandQty, + availableQty: row.availableQty ?? row.stockQty ?? 0, + purchasedQty, + unpurchasedQty: row.unpurchasedQty ?? (demandQty - purchasedQty), + remark: row.remark ?? '' + } + }) as ImportPlanMaterial[] + }) +} + +export function getItemTypeList() { + return request.get('/mes/md/itemtype/list', { params: { pageNum: 1, pageSize: 100 } }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? (Array.isArray(res.data) ? res.data : []) + return rows.map((row: any) => ({ + itemTypeId: row.itemTypeId, + itemTypeName: row.itemTypeName + })) as Array<{ itemTypeId: number; itemTypeName: string }> + }) +} diff --git a/erp-frontend-vue/src/api/rd/ebom.ts b/erp-frontend-vue/src/api/rd/ebom.ts new file mode 100644 index 0000000..7524383 --- /dev/null +++ b/erp-frontend-vue/src/api/rd/ebom.ts @@ -0,0 +1,227 @@ +import request from '../request' + +// ==================== 类型定义 ==================== + +/** EBOM 表头 */ +export interface EbomHeader { + bomId?: number + bomCode?: string + bomName?: string + bomDate?: string + version?: string + versionDesc?: string + status?: string + businessType?: string + businessStatus?: string + drawingNo?: string + itemId?: number + itemCode?: string + itemName?: string + itemSpec?: string + unitName?: string + baseQty?: number + operatorId?: number + operatorName?: string + deptId?: number + deptName?: string + approverId?: number + approverName?: string + approveDate?: string + enableFlag?: string + remark?: string + tenantId?: string + delFlag?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + /** 明细行 (表单提交时携带) */ + lines?: EbomLine[] +} + +/** EBOM 明细行 */ +export interface EbomLine { + lineId?: number + bomId?: number + bomCode?: string + lineNo?: number + bomItemId?: number + bomItemCode?: string + bomItemName?: string + bomItemSpec?: string + unitOfMeasure?: string + unitName?: string + itemOrProduct?: string + quantity?: number + numerator?: number + denominator?: number + lossRate?: number + supplyType?: string + pickType?: string + usageType?: string + routeId?: number + routeName?: string + drawingNo?: string + remark?: string + tenantId?: string + delFlag?: string + /** 前端临时字段 */ + _isNew?: boolean +} + +/** 查询参数 */ +export interface EbomQuery { + bomCode?: string + itemCode?: string + itemName?: string + itemTypeId?: number + status?: string + businessStatus?: string + beginDate?: string + endDate?: string + workType?: number + pageNum?: number + pageSize?: number +} + +/** 列表响应 */ +export interface EbomListResponse { + rows: EbomHeader[] + total: number +} + +// ==================== 常量定义 ==================== + +/** 单据状态映射 */ +export const STATUS_MAP: Record = { + DRAFT: { label: '开立', type: 'info' }, + APPROVED: { label: '审核', type: 'success' }, + REJECTED: { label: '退回', type: 'warning' } +} + +/** 单据状态选项 */ +export const STATUS_OPTIONS = [ + { value: 'DRAFT', label: '开立' }, + { value: 'APPROVED', label: '审核' }, + { value: 'REJECTED', label: '退回' } +] + +/** 业务状态选项 */ +export const BIZ_STATUS_OPTIONS = [ + { value: 'NORMAL', label: '正常' }, + { value: 'PAUSE', label: '暂停' }, + { value: 'CANCEL', label: '取消' } +] + +/** 业务类型选项 */ +export const BIZ_TYPE_OPTIONS = [ + { value: 'MECHANICAL', label: '机械BOM' }, + { value: 'ELECTRICAL', label: '电气BOM' }, + { value: 'ASSEMBLY', label: '装配BOM' }, + { value: 'GENERAL', label: '通用BOM' } +] + +/** 领料方式选项 */ +export const PICK_TYPE_OPTIONS = [ + { value: 'ORDER', label: '按单领用' }, + { value: 'DIRECT', label: '直接领用' } +] + +/** 用量方式选项 */ +export const USAGE_TYPE_OPTIONS = [ + { value: 'RATIO', label: '按比例' }, + { value: 'FIXED', label: '固定用量' } +] + +/** 供应方式选项 */ +export const SUPPLY_TYPE_OPTIONS = [ + { value: 'PURCHASE', label: '采购' }, + { value: 'PRODUCE', label: '生产' }, + { value: 'OUTSOURCE', label: '委外' }, + { value: 'ASSEMBLY', label: '装配' }, + { value: 'PROCESS', label: '加工' } +] + +// ==================== API 函数 ==================== + +const BASE = '/mes/md/bom' +const LINE_BASE = '/mes/md/bom/line' + +/** 查询EBOM列表(单据视图) */ +export function listEbom(query?: EbomQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询EBOM列表(明细视图 - 含物料信息的展开列表) */ +export function listEbomDetail(query?: EbomQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 获取EBOM详情 */ +export function getEbom(bomId: number): Promise<{ data: EbomHeader }> { + return request.get(`${BASE}/${bomId}`) +} + +/** 新增EBOM */ +export function addEbom(data: Partial): Promise<{ data: EbomHeader }> { + return request.post(BASE, data) +} + +/** 修改EBOM */ +export function updateEbom(data: Partial): Promise<{ data: EbomHeader }> { + return request.put(BASE, data) +} + +/** 删除EBOM */ +export function deleteEbom(ids: number | number[]): Promise { + const idStr = Array.isArray(ids) ? ids.join(',') : String(ids) + return request.delete(`${BASE}/${idStr}`) +} + +/** 审核EBOM */ +export function approveEbom(bomId: number): Promise { + return request.put(`${BASE}/approve`, { bomId }) +} + +/** 反审核EBOM */ +export function unapproveEbom(bomId: number): Promise { + return request.put(`${BASE}/reject`, { bomId }) +} + +/** 查询BOM明细列表 */ +export function listEbomLines(query?: { bomCode?: string; pageNum?: number; pageSize?: number }): Promise<{ rows: EbomLine[]; total: number }> { + return request.get(`${LINE_BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 新增BOM明细 */ +export function addEbomLine(data: Partial): Promise<{ data: EbomLine }> { + return request.post(LINE_BASE, data) +} + +/** 修改BOM明细 */ +export function updateEbomLine(data: Partial): Promise<{ data: EbomLine }> { + return request.put(LINE_BASE, data) +} + +/** 删除BOM明细 */ +export function deleteEbomLine(ids: number | number[]): Promise { + const idStr = Array.isArray(ids) ? ids.join(',') : String(ids) + return request.delete(`${LINE_BASE}/${idStr}`) +} + +/** 导出EBOM列表(GET,后端与 POST /export 行为一致) */ +export function exportEbom(query?: EbomQuery): Promise { + return request.get(`${BASE}/export`, { + params: query, + responseType: 'blob' + }) +} diff --git a/erp-frontend-vue/src/api/reject.ts b/erp-frontend-vue/src/api/reject.ts new file mode 100644 index 0000000..4470d22 --- /dev/null +++ b/erp-frontend-vue/src/api/reject.ts @@ -0,0 +1,91 @@ +import request from './request' + +export interface RejectLine { + lineId: number + rejectId: number + rejectCode: string + lineNo: number + checkinLineId?: number + trackCode?: string + itemId: number + itemCode: string + itemName: string + specification?: string + unitName?: string + quantity: number + outQuantity?: number + returnQuantity?: number + reason?: string + remark?: string +} + +export interface Reject { + rejectId: number + rejectCode: string + rejectDate: string + status: string + businessType: string + supplierId: number + supplierName: string + checkinId?: number + checkinCode?: string + totalQuantity?: number + outQuantity?: number + returnQuantity?: number + reason?: string + remark?: string + operatorName?: string + approverName?: string + approveDate?: string + lines: RejectLine[] + createTime?: string +} + +export interface RejectQuery { + trackCode?: string + rejectCode?: string + itemCode?: string + itemName?: string + beginDate?: string + endDate?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface RejectListResponse { + rows: Reject[] + total: number +} + +// 获取采购退货单列表 +export function getRejectList(params: RejectQuery): Promise { + return request.get('/erp/po/reject/list', { params }).then((res: any) => { + return { + rows: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取采购退货单详情 +export function getRejectDetail(rejectId: number): Promise { + return request.get(`/erp/po/reject/${rejectId}`).then((res: any) => { + return res.data + }) +} + +// 新增采购退货单 +export function createReject(data: Partial): Promise { + return request.post('/erp/po/reject', data) +} + +// 更新采购退货单 +export function updateReject(data: Partial): Promise { + return request.put('/erp/po/reject', data) +} + +// 删除采购退货单 +export function deleteReject(rejectIds: string): Promise { + return request.delete(`/erp/po/reject/${rejectIds}`) +} diff --git a/erp-frontend-vue/src/api/request.ts b/erp-frontend-vue/src/api/request.ts new file mode 100644 index 0000000..d938132 --- /dev/null +++ b/erp-frontend-vue/src/api/request.ts @@ -0,0 +1,105 @@ +import axios from 'axios' +import { ElMessage, ElMessageBox } from 'element-plus' +import { getToken, removeToken } from '@/utils/auth' +import router from '@/router' + +// 是否显示重新登录弹窗 +let isReloginShowing = false + +const request = axios.create({ + baseURL: '', + timeout: 30000 +}) + +// 请求拦截器 +request.interceptors.request.use( + (config) => { + // 根据URL前缀动态设置baseURL + // 支持 /mes/*、/erp/* 和 /system/* 三种API地址 + if (config.url) { + if (config.url.startsWith('/mes/')) { + // /mes/* 接口直接使用,不添加额外前缀 + // 保持原样 + } else if (config.url.startsWith('/erp/')) { + // /erp/* 接口直接使用,不添加额外前缀 + // 保持原样 + } else if (config.url.startsWith('/system/')) { + // /system/* 接口直接使用,不添加额外前缀(组织架构模块) + // 保持原样 + } else { + // 其他接口默认添加 /erp 前缀 + config.url = '/erp' + config.url + } + } + + // 添加 Token 到请求头 + const token = getToken() + if (token) { + config.headers = config.headers || {} + config.headers['Authorization'] = `Bearer ${token}` + } + + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 响应拦截器 +request.interceptors.response.use( + (response) => { + const res = response.data + const code = res.code || 200 + + // 处理 401 未授权 + if (code === 401) { + if (!isReloginShowing) { + isReloginShowing = true + ElMessageBox.confirm( + '登录状态已过期,您可以继续留在该页面,或者重新登录', + '系统提示', + { + confirmButtonText: '重新登录', + cancelButtonText: '取消', + type: 'warning' + } + ).then(() => { + isReloginShowing = false + removeToken() + router.push('/login') + }).catch(() => { + isReloginShowing = false + }) + } + return Promise.reject(new Error('无效的会话,或者会话已过期,请重新登录。')) + } + + // 处理其他错误码 + if (code !== 200) { + const msg = res.msg || res.message || '请求失败' + ElMessage.error(msg) + return Promise.reject(new Error(msg)) + } + + return res + }, + (error) => { + console.error('Request error:', error) + let message = error.message || '网络错误' + + if (message === 'Network Error') { + message = '后端接口连接异常' + } else if (message.includes('timeout')) { + message = '系统接口请求超时' + } else if (message.includes('Request failed with status code')) { + const status = message.substring(message.length - 3) + message = `系统接口 ${status} 异常` + } + + ElMessage.error(message) + return Promise.reject(error) + } +) + +export default request diff --git a/erp-frontend-vue/src/api/saleback.ts b/erp-frontend-vue/src/api/saleback.ts new file mode 100644 index 0000000..51177fd --- /dev/null +++ b/erp-frontend-vue/src/api/saleback.ts @@ -0,0 +1,105 @@ +import request from './request' + +export interface SalebackLine { + lineId?: number + id?: number + salebackId?: number + salebackCode?: string + deliverLineId?: number + itemId?: number + itemCode?: string + materialCode?: string + itemName?: string + materialName?: string + specification?: string + spec?: string + unitOfMeasure?: string + unit?: string + returnQty: number + reason?: string +} + +export interface Saleback { + salebackId?: number + id?: number + salebackCode?: string + code?: string + clientId?: number + customerId?: number + clientCode?: string + customerCode?: string + clientName?: string + customerName?: string + returnDate: string + status: string + totalQty?: number + reason?: string + remark?: string + lines?: SalebackLine[] + createTime?: string +} + +export interface SalebackQuery { + code?: string + customerName?: string + status?: string + page?: number + pageSize?: number +} + +export interface SalebackListResponse { + list: Saleback[] + total: number +} + +// 获取退货单列表 +export function getSalebackList(params: SalebackQuery): Promise { + return request.get('/erp/sl/saleback/list', { params }).then((res: any) => { + return { + list: res.rows || [], + total: res.total || 0 + } + }) +} + +// 获取退货单详情 +export function getSalebackDetail(id: number): Promise { + return request.get(`/erp/sl/saleback/${id}`).then((res: any) => { + return res.data + }) +} + +// 新增退货单 +export function createSaleback(data: Partial): Promise { + return request.post('/erp/sl/saleback', data) +} + +// 更新退货单 +export function updateSaleback(id: number, data: Partial): Promise { + return request.put('/erp/sl/saleback', { ...data, salebackId: id }) +} + +// 删除退货单 +export function deleteSaleback(id: number): Promise { + return request.delete(`/erp/sl/saleback/${id}`) +} + +// 审核退货单 +export function auditSaleback(id: number): Promise { + return request.post(`/erp/sl/saleback/audit/${id}`) +} + +// 反审核退货单 +export function unauditSaleback(id: number): Promise { + return request.post(`/erp/sl/saleback/unaudit/${id}`) +} + +// 引入发货单 +export function getIntroduceDeliverList(params: any): Promise { + return request.get('/erp/sl/saleback/introduce', { params }).then((res: any) => { + return { + list: res.rows || [], + total: res.total || 0 + } + }) +} diff --git a/erp-frontend-vue/src/api/salesOrder.ts b/erp-frontend-vue/src/api/salesOrder.ts new file mode 100644 index 0000000..cbb4b82 --- /dev/null +++ b/erp-frontend-vue/src/api/salesOrder.ts @@ -0,0 +1,268 @@ +import request from './request' + +// ============ 类型定义 ============ + +/** 订单明细行 */ +export interface SalesOrderLine { + lineId?: number + orderId?: number + itemId?: number + itemCode?: string + itemName?: string + specification?: string + unitOfMeasure?: string + quantity?: number // 可选,新增时默认为空 + unitPrice?: number + amount?: number + deliveredQty?: number + qualityReq?: string + remark?: string + delFlag?: string +} + +/** 销售订单主表 */ +export interface SalesOrder { + orderId?: number + orderCode?: string + orderName?: string + orderDate?: string + orderStatus?: string + // 客户信息 + clientId?: number + clientCode?: string + clientName?: string + // 业务信息 + contractNo?: string + bizType?: string + bizStatus?: string + salesType?: string + supplyMode?: string + deliveryStatus?: string + // 部门/人员 + deptId?: number + deptName?: string + salesmanId?: number + salesmanName?: string + operatorId?: number + operatorName?: string + auditorId?: number + auditorName?: string + auditDate?: string + // 收货信息 + receiver?: string + receiverPhone?: string + receiverAddress?: string + destCountry?: string + paymentTerms?: string + deliveryDate?: string + // 金额 + totalAmount?: number + currency?: string + // 其他 + remark?: string + delFlag?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + // 明细行 + lines?: SalesOrderLine[] + slaveList?: SalesOrderLine[] +} + +/** 明细视图数据(展开物料维度) */ +export interface SalesOrderDetailView { + orderId: number + orderCode: string + orderDate: string + orderStatus: string + contractNo?: string + clientId: number + clientName: string + salesmanName?: string + salesType?: string + // 物料信息 + lineId: number + itemId: number + itemCode: string + itemName: string + specification?: string + quantity: number + unitPrice?: number + amount?: number + deliveredQty?: number +} + +/** 查询参数 */ +export interface SalesOrderQuery { + orderCode?: string + contractNo?: string + clientName?: string + salesmanName?: string + salesType?: string + itemName?: string + orderStatus?: string + beginOrderDate?: string + endOrderDate?: string + pageNum?: number + pageSize?: number +} + +/** 分页响应 */ +export interface SalesOrderListResponse { + list: SalesOrder[] + total: number +} + +export interface SalesOrderDetailListResponse { + list: SalesOrderDetailView[] + total: number +} + +// ============ 状态映射 ============ + +export const ORDER_STATUS_MAP: Record = { + 'DRAFT': { label: '草稿', type: 'info' }, + '开立': { label: '开立', type: '' }, + '审核': { label: '审核', type: 'success' }, + '退回': { label: '退回', type: 'warning' }, + '关闭': { label: '关闭', type: 'danger' } +} + +export const SALES_TYPE_OPTIONS = [ + { value: 'factory', label: '工厂订单' }, + { value: 'stock', label: '库存供应' }, + { value: 'purchase', label: '买进卖出' } +] + +export const SUPPLY_MODE_OPTIONS = [ + { value: 'plan', label: '计划生产' }, + { value: 'stock', label: '库存供应' } +] + +export const DELIVERY_STATUS_OPTIONS = [ + { value: 'estimate', label: '预计' }, + { value: 'confirm', label: '确认' } +] + +export const BIZ_TYPE_OPTIONS = [ + { value: 'sales_order', label: '销售订单' } +] + +export const BIZ_STATUS_OPTIONS = [ + { value: 'normal', label: '正常' }, + { value: 'suspend', label: '挂起' } +] + +// ============ API 接口 ============ + +const BASE = '/erp/sl/order' + +/** 获取销售订单列表(单据视图) */ +export function getSalesOrderList(params: SalesOrderQuery): Promise { + return request.get(`${BASE}/list`, { params }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { list: rows, total } + }) +} + +/** 获取销售订单明细列表(明细视图,展开物料维度) */ +export function getSalesOrderDetailList(params: SalesOrderQuery): Promise { + return request.get(`${BASE}/lineList`, { params }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { list: rows, total } + }) +} + +/** 获取销售订单详情(含明细行) */ +export function getSalesOrderDetail(orderId: number): Promise { + return request.get(`${BASE}/${orderId}`).then((res: any) => { + const data = res.data ?? res + // 统一明细字段名 + if (data.slaveList && !data.lines) { + data.lines = data.slaveList + } + // 后端返回 salesUserId/salesUserName,前端表单使用 salesmanId/salesmanName,便于下拉回显 + if (data.salesUserId !== undefined) data.salesmanId = data.salesUserId + if (data.salesUserName !== undefined) data.salesmanName = data.salesUserName + return data + }) +} + +/** 构建保存用 payload:明细转 slaveList,销售人员字段对齐后端 salesUserId/salesUserName */ +function buildOrderPayload(data: Partial): Record { + const payload: Record = { ...data } + if (payload.lines) { + payload.slaveList = payload.lines + delete payload.lines + } + // 后端实体为 salesUserId / salesUserName,前端表单为 salesmanId / salesmanName,此处统一发给后端 + if (payload.salesmanId !== undefined) payload.salesUserId = payload.salesmanId + if (payload.salesmanName !== undefined) payload.salesUserName = payload.salesmanName + return payload +} + +/** 新增销售订单 */ +export function createSalesOrder(data: Partial): Promise<{ orderId: number }> { + return request.post(BASE, buildOrderPayload(data)).then((res: any) => res.data ?? res) +} + +/** 更新销售订单 */ +export function updateSalesOrder(data: Partial): Promise { + return request.put(BASE, buildOrderPayload(data)) +} + +/** 删除销售订单(支持批量) */ +export function deleteSalesOrder(orderIds: number | number[]): Promise { + const ids = Array.isArray(orderIds) ? orderIds.join(',') : String(orderIds) + return request.delete(`${BASE}/${ids}`) +} + +/** 审核销售订单(支持批量) */ +export function auditSalesOrder(orderIds: number | number[]): Promise { + if (orderIds == null || (Array.isArray(orderIds) && orderIds.length === 0)) { + return Promise.reject(new Error('订单ID不能为空')) + } + const ids = Array.isArray(orderIds) ? orderIds.join(',') : String(orderIds) + return request.post(`${BASE}/audit/${ids}`) +} + +/** 反审核销售订单(支持批量) */ +export function unauditSalesOrder(orderIds: number | number[]): Promise { + if (orderIds == null || (Array.isArray(orderIds) && orderIds.length === 0)) { + return Promise.reject(new Error('订单ID不能为空')) + } + const ids = Array.isArray(orderIds) ? orderIds.join(',') : String(orderIds) + return request.post(`${BASE}/unaudit/${ids}`) +} + +/** 导出销售订单 */ +export function exportSalesOrder(params: SalesOrderQuery): Promise { + return request.get(`${BASE}/export`, { + params, + responseType: 'blob' + }) +} + +// ============ 辅助接口 ============ + +/** 从销售合同引入订单明细 */ +export function importFromContract(contractId: number): Promise<{ lines: SalesOrderLine[] }> { + return request.get(`/erp/sl/contract/${contractId}/lines`).then((res: any) => { + return { lines: res.data ?? res.rows ?? [] } + }) +} + +/** 获取可引入的合同列表 */ +export function getAvailableContracts(clientId?: number): Promise { + return request.get('/erp/sl/contract/available', { params: { clientId } }).then((res: any) => { + return res.rows ?? res.data ?? [] + }) +} + +/** 生成订单编码(可选,后端自动生成时不需要) */ +export function generateOrderCode(): Promise { + return request.get(`${BASE}/generateCode`).then((res: any) => res.data ?? res) +} diff --git a/erp-frontend-vue/src/api/supplier.ts b/erp-frontend-vue/src/api/supplier.ts new file mode 100644 index 0000000..79afa6b --- /dev/null +++ b/erp-frontend-vue/src/api/supplier.ts @@ -0,0 +1,155 @@ +import request from './request' + +// ============ 类型定义 ============ + +/** 供应商主数据 */ +export interface Supplier { + vendorId?: number + vendorCode?: string + vendorName?: string + vendorNick?: string + vendorEn?: string + vendorDes?: string + vendorType?: string + vendorLevel?: string + vendorScore?: number + vendorLogo?: string + address?: string + province?: string + city?: string + zipCode?: string + website?: string + email?: string + tel?: string + fax?: string + contact1?: string + contact1Tel?: string + contact1Email?: string + contact2?: string + contact2Tel?: string + contact2Email?: string + creditCode?: string + bankName?: string + bankAccount?: string + bankAddress?: string + taxNo?: string + supplyItems?: string + remark?: string + enableFlag?: string + delFlag?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +/** 查询参数 */ +export interface SupplierQuery { + vendorCode?: string + vendorName?: string + vendorNick?: string + vendorEn?: string + vendorType?: string + vendorLevel?: string + enableFlag?: string + pageNum?: number + pageSize?: number +} + +/** 分页响应 */ +export interface SupplierListResponse { + rows: Supplier[] + total: number +} + +// ============ 状态枚举 ============ + +/** 供应商等级选项 */ +export const VENDOR_LEVEL_OPTIONS = [ + { value: 'A', label: 'A级' }, + { value: 'B', label: 'B级' }, + { value: 'C', label: 'C级' }, + { value: 'D', label: 'D级' } +] + +/** 供应商类型选项 */ +export const VENDOR_TYPE_OPTIONS = [ + { value: 'CORE', label: '核心供应商' }, + { value: 'NORMAL', label: '普通供应商' }, + { value: 'TEMP', label: '临时供应商' } +] + +/** 是否启用选项 */ +export const ENABLE_FLAG_OPTIONS = [ + { value: 'Y', label: '启用' }, + { value: 'N', label: '停用' } +] + +// ============ API 接口 ============ + +const BASE = '/mes/md/vendor' + +/** 获取供应商列表 */ +export function getSupplierList(params: SupplierQuery): Promise { + return request.get(`${BASE}/list`, { params }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { rows, total } + }) +} + +/** 获取供应商详情 */ +export function getSupplierDetail(vendorId: number): Promise { + return request.get(`${BASE}/${vendorId}`).then((res: any) => { + return res.data ?? res + }) +} + +/** 新增供应商 */ +export function createSupplier(data: Partial): Promise<{ vendorId: number }> { + return request.post(BASE, data).then((res: any) => res.data ?? res) +} + +/** 更新供应商 */ +export function updateSupplier(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除供应商(支持批量) */ +export function deleteSupplier(vendorIds: number | number[] | string): Promise { + const ids = Array.isArray(vendorIds) ? vendorIds.join(',') : String(vendorIds) + return request.delete(`${BASE}/${ids}`) +} + +/** 导出供应商列表 */ +export function exportSupplier(params: SupplierQuery): Promise { + return request.get(`${BASE}/export`, { + params, + responseType: 'blob' + }) +} + +/** 下载导入模板 */ +export function downloadImportTemplate(): Promise { + return request.get(`${BASE}/importTemplate`, { + responseType: 'blob' + }) +} + +/** 导入供应商数据 */ +export function importSupplier(file: File, updateSupport: boolean = false): Promise { + const formData = new FormData() + formData.append('file', file) + return request.post(`${BASE}/importData?updateSupport=${updateSupport}`, formData, { + headers: { 'Content-Type': 'multipart/form-data' } + }) +} + +/** 获取供应商选项列表(用于下拉选择) */ +export function getSupplierOptions(): Promise { + return request.get(`${BASE}/list`, { + params: { enableFlag: 'Y', pageNum: 1, pageSize: 1000 } + }).then((res: any) => { + return res.rows ?? res.data?.rows ?? [] + }) +} diff --git a/erp-frontend-vue/src/api/system/dept.ts b/erp-frontend-vue/src/api/system/dept.ts new file mode 100644 index 0000000..c2527d6 --- /dev/null +++ b/erp-frontend-vue/src/api/system/dept.ts @@ -0,0 +1,91 @@ +import request from '../request' + +export interface Dept { + deptId?: number + parentId?: number + ancestors?: string + deptCode?: string + deptName?: string + orderNum?: number + leader?: string + phone?: string + email?: string + status?: string + delFlag?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + children?: Dept[] +} + +export interface DeptQuery { + deptName?: string + status?: string +} + +const BASE = '/system/dept' + +/** 查询部门列表 */ +export function listDept(query?: DeptQuery): Promise<{ data: Dept[] }> { + return request.get(`${BASE}/list`, { params: query }) +} + +/** 查询部门列表(排除节点) */ +export function listDeptExcludeChild(deptId: number): Promise<{ data: Dept[] }> { + return request.get(`${BASE}/list/exclude/${deptId}`) +} + +/** 查询部门详细 */ +export function getDept(deptId: number): Promise<{ data: Dept }> { + return request.get(`${BASE}/${deptId}`) +} + +/** 查询部门下拉树结构 */ +export function getDeptTreeselect(): Promise<{ data: any[] }> { + return request.get(`${BASE}/treeselect`) +} + +/** 根据角色ID查询部门树结构 */ +export function getRoleDeptTreeselect(roleId: number): Promise<{ data: any }> { + return request.get(`${BASE}/roleDeptTreeselect/${roleId}`) +} + +/** 新增部门 */ +export function addDept(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改部门 */ +export function updateDept(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除部门 */ +export function delDept(deptId: number): Promise { + return request.delete(`${BASE}/${deptId}`) +} + +/** 构建部门树 */ +export function handleTree(data: Dept[], idField = 'deptId', parentField = 'parentId'): Dept[] { + const map = new Map() + const result: Dept[] = [] + + data.forEach(item => { + map.set(item[idField as keyof Dept] as number, { ...item, children: [] }) + }) + + data.forEach(item => { + const current = map.get(item[idField as keyof Dept] as number)! + const parentId = item[parentField as keyof Dept] as number + if (parentId && map.has(parentId)) { + const parent = map.get(parentId)! + parent.children = parent.children || [] + parent.children.push(current) + } else { + result.push(current) + } + }) + + return result +} diff --git a/erp-frontend-vue/src/api/system/post.ts b/erp-frontend-vue/src/api/system/post.ts new file mode 100644 index 0000000..d0b1b9a --- /dev/null +++ b/erp-frontend-vue/src/api/system/post.ts @@ -0,0 +1,58 @@ +import request from '../request' + +export interface Post { + postId?: number + postCode?: string + postName?: string + postSort?: number + status?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + remark?: string +} + +export interface PostQuery { + postCode?: string + postName?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface PostListResponse { + rows: Post[] + total: number +} + +const BASE = '/system/post' + +/** 查询岗位列表 */ +export function listPost(query?: PostQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询岗位详细 */ +export function getPost(postId: number): Promise<{ data: Post }> { + return request.get(`${BASE}/${postId}`) +} + +/** 新增岗位 */ +export function addPost(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改岗位 */ +export function updatePost(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除岗位 */ +export function delPost(postId: number | number[]): Promise { + const ids = Array.isArray(postId) ? postId.join(',') : String(postId) + return request.delete(`${BASE}/${ids}`) +} diff --git a/erp-frontend-vue/src/api/system/role.ts b/erp-frontend-vue/src/api/system/role.ts new file mode 100644 index 0000000..042e404 --- /dev/null +++ b/erp-frontend-vue/src/api/system/role.ts @@ -0,0 +1,99 @@ +import request from '../request' + +export interface Role { + roleId?: number + roleName?: string + roleKey?: string + roleSort?: number + dataScope?: string + menuCheckStrictly?: boolean + deptCheckStrictly?: boolean + status?: string + delFlag?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + remark?: string + menuIds?: number[] + deptIds?: number[] +} + +export interface RoleQuery { + roleName?: string + roleKey?: string + status?: string + pageNum?: number + pageSize?: number +} + +export interface RoleListResponse { + rows: Role[] + total: number +} + +const BASE = '/system/role' + +/** 查询角色列表 */ +export function listRole(query?: RoleQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询角色详细 */ +export function getRole(roleId: number): Promise<{ data: Role }> { + return request.get(`${BASE}/${roleId}`) +} + +/** 新增角色 */ +export function addRole(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改角色 */ +export function updateRole(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 角色数据权限 */ +export function dataScope(data: Partial): Promise { + return request.put(`${BASE}/dataScope`, data) +} + +/** 角色状态修改 */ +export function changeRoleStatus(roleId: number, status: string): Promise { + return request.put(`${BASE}/changeStatus`, { roleId, status }) +} + +/** 删除角色 */ +export function delRole(roleId: number | number[]): Promise { + const ids = Array.isArray(roleId) ? roleId.join(',') : String(roleId) + return request.delete(`${BASE}/${ids}`) +} + +/** 查询角色已授权用户列表 */ +export function allocatedUserList(query: any): Promise { + return request.get(`${BASE}/authUser/allocatedList`, { params: query }) +} + +/** 查询角色未授权用户列表 */ +export function unallocatedUserList(query: any): Promise { + return request.get(`${BASE}/authUser/unallocatedList`, { params: query }) +} + +/** 取消用户授权角色 */ +export function authUserCancel(data: any): Promise { + return request.put(`${BASE}/authUser/cancel`, data) +} + +/** 批量取消用户授权角色 */ +export function authUserCancelAll(data: any): Promise { + return request.put(`${BASE}/authUser/cancelAll`, data) +} + +/** 授权用户选择 */ +export function authUserSelectAll(data: any): Promise { + return request.put(`${BASE}/authUser/selectAll`, data) +} diff --git a/erp-frontend-vue/src/api/system/user.ts b/erp-frontend-vue/src/api/system/user.ts new file mode 100644 index 0000000..80b84a6 --- /dev/null +++ b/erp-frontend-vue/src/api/system/user.ts @@ -0,0 +1,123 @@ +import request from '../request' + +export interface User { + userId?: number + deptId?: number + userName?: string + nickName?: string + userType?: string + email?: string + phonenumber?: string + sex?: string + avatar?: string + password?: string + status?: string + delFlag?: string + loginIp?: string + loginDate?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + remark?: string + dept?: { + deptId?: number + deptName?: string + } + roles?: any[] + roleIds?: number[] + postIds?: number[] +} + +export interface UserQuery { + userName?: string + phonenumber?: string + status?: string + deptId?: number + pageNum?: number + pageSize?: number + beginTime?: string + endTime?: string +} + +export interface UserListResponse { + rows: User[] + total: number +} + +const BASE = '/system/user' + +/** 查询用户列表 */ +export function listUser(query?: UserQuery): Promise { + return request.get(`${BASE}/list`, { params: query }).then((res: any) => ({ + rows: res.rows || [], + total: res.total || 0 + })) +} + +/** 查询用户详细 */ +export function getUser(userId?: number): Promise<{ + data: User + roles: any[] + posts: any[] + postIds: number[] + roleIds: number[] +}> { + return request.get(userId ? `${BASE}/${userId}` : BASE) +} + +/** 新增用户 */ +export function addUser(data: Partial): Promise { + return request.post(BASE, data) +} + +/** 修改用户 */ +export function updateUser(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除用户 */ +export function delUser(userId: number | number[]): Promise { + const ids = Array.isArray(userId) ? userId.join(',') : String(userId) + return request.delete(`${BASE}/${ids}`) +} + +/** 用户密码重置 */ +export function resetUserPwd(userId: number, password: string): Promise { + return request.put(BASE + '/resetPwd', { userId, password }) +} + +/** 用户状态修改 */ +export function changeUserStatus(userId: number, status: string): Promise { + return request.put(BASE + '/changeStatus', { userId, status }) +} + +/** 查询用户个人信息 */ +export function getUserProfile(): Promise<{ data: User }> { + return request.get(`${BASE}/profile`) +} + +/** 修改用户个人信息 */ +export function updateUserProfile(data: Partial): Promise { + return request.put(`${BASE}/profile`, data) +} + +/** 用户密码重置 */ +export function updateUserPwd(oldPassword: string, newPassword: string): Promise { + return request.put(`${BASE}/profile/updatePwd`, { oldPassword, newPassword }) +} + +/** 用户头像上传 */ +export function uploadAvatar(data: FormData): Promise<{ imgUrl: string }> { + return request.post(`${BASE}/profile/avatar`, data) +} + +/** 查询授权角色 */ +export function getAuthRole(userId: number): Promise<{ user: User; roles: any[] }> { + return request.get(`${BASE}/authRole/${userId}`) +} + +/** 保存授权角色 */ +export function updateAuthRole(data: { userId: number; roleIds: string }): Promise { + return request.put(`${BASE}/authRole`, data) +} diff --git a/erp-frontend-vue/src/api/warehouse/issue.ts b/erp-frontend-vue/src/api/warehouse/issue.ts new file mode 100644 index 0000000..60ef750 --- /dev/null +++ b/erp-frontend-vue/src/api/warehouse/issue.ts @@ -0,0 +1,222 @@ +import request from '../request' + +// ============ 类型定义 ============ + +/** 生产领料单头 */ +export interface IssueHeader { + issueId?: number + issueCode?: string + issueName?: string + workstationId?: number + workstationCode?: string + workstationName?: string + workorderId?: number + workorderCode?: string + taskId?: number + taskCode?: string + clientId?: number + clientCode?: string + clientName?: string + clientNick?: string + requiredTime?: string + issueDate?: string + status?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +/** 查询参数 */ +export interface IssueHeaderQuery { + issueCode?: string + issueName?: string + workorderCode?: string + workstationName?: string + status?: string + beginIssueDate?: string + endIssueDate?: string + pageNum?: number + pageSize?: number +} + +/** 分页响应 */ +export interface IssueHeaderListResponse { + rows: IssueHeader[] + total: number +} + +// ============ 状态映射 ============ + +export const STATUS_MAP: Record = { + 'PREPARE': { label: '草稿', type: 'info' }, + 'CONFIRMED': { label: '已确认', type: '' }, + 'APPROVED': { label: '已审核', type: 'success' }, + 'FINISHED': { label: '已完成', type: 'success' } +} + +// ============ API 接口 ============ + +const BASE = '/mes/wm/issueheader' + +/** 查询领料单列表 */ +export function getIssueHeaderList(params: IssueHeaderQuery): Promise { + return request.get(`${BASE}/list`, { params }).then((res: any) => { + const rows = res.rows ?? res.data?.rows ?? [] + const total = res.total ?? res.data?.total ?? 0 + return { rows, total } + }) +} + +/** 获取领料单详情 */ +export function getIssueHeaderDetail(issueId: number): Promise { + return request.get(`${BASE}/${issueId}`).then((res: any) => { + return res.data ?? res + }) +} + +/** 将日期字段规范为 yyyy-MM-dd HH:mm:ss(后端 JsonFormat 要求) */ +function normalizeDateFields(data: Partial): Partial { + const out = { ...data } + if (typeof out.issueDate === 'string' && out.issueDate.length === 10) { + out.issueDate = out.issueDate + ' 00:00:00' + } + if (typeof out.requiredTime === 'string' && out.requiredTime.length === 10) { + out.requiredTime = out.requiredTime + ' 00:00:00' + } + return out +} + +/** 新增领料单,返回服务端生成的领料单对象(含 issueId、issueCode) */ +export function createIssueHeader(data: Partial): Promise { + return request.post(BASE, normalizeDateFields(data)).then((res: any) => res.data ?? res) +} + +/** 修改领料单 */ +export function updateIssueHeader(data: Partial): Promise { + return request.put(BASE, normalizeDateFields(data)) +} + +/** 删除领料单(支持批量) */ +export function deleteIssueHeader(ids: number | number[] | string): Promise { + const idStr = Array.isArray(ids) ? ids.join(',') : String(ids) + return request.delete(`${BASE}/${idStr}`) +} + +/** 执行出库 */ +export function executeIssue(issueId: number): Promise { + return request.put(`${BASE}/${issueId}`) +} + +/** 校验领料数量与明细数量匹配 */ +export function checkQuantity(issueId: number): Promise { + return request.get(`${BASE}/checkQuantity/${issueId}`).then((res: any) => { + return res.data ?? res + }) +} + +/** 导出领料单 */ +export function exportIssueHeader(params: IssueHeaderQuery): Promise { + return request.post(`${BASE}/export`, params, { responseType: 'blob' }) +} + +// ============ 辅助选择 API ============ + +/** 工单列表(选择弹窗) */ +export function getWorkorderSelectList(params?: { + workorderCode?: string + workorderName?: string + productName?: string + pageNum?: number + pageSize?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/pro/workorder/list', { + params: { pageNum: 1, pageSize: 100, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +/** 物料列表(选择弹窗) */ +export function getItemSelectList(params?: { + itemCode?: string + itemName?: string + pageNum?: number + pageSize?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/md/mditem/list', { + params: { pageNum: 1, pageSize: 100, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +/** 客户列表(选择弹窗) */ +export function getClientSelectList(params?: { + clientCode?: string + clientName?: string + pageNum?: number + pageSize?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/md/client/list', { + params: { pageNum: 1, pageSize: 100, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +/** 工作站列表(选择弹窗) */ +export function getWorkstationSelectList(params?: { + workstationCode?: string + workstationName?: string + pageNum?: number + pageSize?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/md/workstation/list', { + params: { pageNum: 1, pageSize: 100, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +/** 仓库列表 */ +export function getWarehouseList(params?: { + warehouseCode?: string + warehouseName?: string +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/wm/warehouse/list', { + params: { pageNum: 1, pageSize: 200, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +/** 库区列表(按仓库筛选) */ +export function getLocationList(params?: { + warehouseId?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/wm/location/list', { + params: { pageNum: 1, pageSize: 200, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +/** 库位列表(按库区筛选) */ +export function getAreaList(params?: { + locationId?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/wm/area/list', { + params: { pageNum: 1, pageSize: 200, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} diff --git a/erp-frontend-vue/src/api/warehouse/issueDetail.ts b/erp-frontend-vue/src/api/warehouse/issueDetail.ts new file mode 100644 index 0000000..9914c2a --- /dev/null +++ b/erp-frontend-vue/src/api/warehouse/issueDetail.ts @@ -0,0 +1,69 @@ +import request from '../request' + +// ============ 类型定义 ============ + +/** 出库明细 */ +export interface IssueDetail { + detailId?: number + issueId?: number + lineId?: number + materialStockId?: string + itemId?: number + itemCode?: string + itemName?: string + specification?: string + unitOfMeasure?: string + unitName?: string + quantity?: number + batchId?: number + batchCode?: string + warehouseId?: number + warehouseCode?: string + warehouseName?: string + locationId?: number + locationCode?: string + locationName?: string + areaId?: number + areaCode?: string + areaName?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +// ============ API 接口 ============ + +const BASE = '/mes/wm/issuedetail' + +/** 查询出库明细列表 */ +export function getIssueDetailList(params: { + issueId?: number + lineId?: number + pageNum?: number + pageSize?: number +}): Promise<{ rows: IssueDetail[]; total: number }> { + return request.get(`${BASE}/list`, { + params: { pageNum: 1, pageSize: 200, ...params } + }).then((res: any) => ({ + rows: res.rows ?? res.data?.rows ?? [], + total: res.total ?? res.data?.total ?? 0 + })) +} + +/** 新增出库明细 */ +export function createIssueDetail(data: Partial): Promise { + return request.post(BASE, data).then((res: any) => res.data ?? res) +} + +/** 修改出库明细 */ +export function updateIssueDetail(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除出库明细(支持批量) */ +export function deleteIssueDetail(ids: number | number[] | string): Promise { + const idStr = Array.isArray(ids) ? ids.join(',') : String(ids) + return request.delete(`${BASE}/${idStr}`) +} diff --git a/erp-frontend-vue/src/api/warehouse/issueLine.ts b/erp-frontend-vue/src/api/warehouse/issueLine.ts new file mode 100644 index 0000000..fc97aa4 --- /dev/null +++ b/erp-frontend-vue/src/api/warehouse/issueLine.ts @@ -0,0 +1,59 @@ +import request from '../request' + +// ============ 类型定义 ============ + +/** 领料行 */ +export interface IssueLine { + lineId?: number + issueId?: number + materialStockId?: number + itemId?: number + itemCode?: string + itemName?: string + specification?: string + unitOfMeasure?: string + unitName?: string + quantityIssued?: number + quantity?: number + batchId?: number + batchCode?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +// ============ API 接口 ============ + +const BASE = '/mes/wm/issueline' + +/** 查询领料行列表 */ +export function getIssueLineList(params: { + issueId?: number + pageNum?: number + pageSize?: number +}): Promise<{ rows: IssueLine[]; total: number }> { + return request.get(`${BASE}/list`, { + params: { pageNum: 1, pageSize: 200, ...params } + }).then((res: any) => ({ + rows: res.rows ?? res.data?.rows ?? [], + total: res.total ?? res.data?.total ?? 0 + })) +} + +/** 新增领料行 */ +export function createIssueLine(data: Partial): Promise { + return request.post(BASE, data).then((res: any) => res.data ?? res) +} + +/** 修改领料行 */ +export function updateIssueLine(data: Partial): Promise { + return request.put(BASE, data) +} + +/** 删除领料行(支持批量) */ +export function deleteIssueLine(ids: number | number[] | string): Promise { + const idStr = Array.isArray(ids) ? ids.join(',') : String(ids) + return request.delete(`${BASE}/${idStr}`) +} diff --git a/erp-frontend-vue/src/api/workOrder.ts b/erp-frontend-vue/src/api/workOrder.ts new file mode 100644 index 0000000..7c1a57c --- /dev/null +++ b/erp-frontend-vue/src/api/workOrder.ts @@ -0,0 +1,305 @@ +import request from './request' + +// ============ 类型定义 ============ + +/** 生产工单 BOM 物料行 */ +export interface WorkOrderBom { + lineId?: number + workorderId?: number + itemId?: number + itemCode?: string + itemName?: string + itemSpc?: string + unitOfMeasure?: string + unitName?: string + itemOrProduct?: string + quantity?: number + remark?: string +} + +/** 生产工单主表 */ +export interface WorkOrder { + workorderId?: number + workorderCode?: string + workorderName?: string + workorderType?: string + orderSource?: string + sourceCode?: string + productId?: number + productCode?: string + productName?: string + productSpc?: string + unitOfMeasure?: string + unitName?: string + routeId?: number + routeCode?: string + routeName?: string + batchCode?: string + quantity?: number + quantityProduced?: number + quantityChanged?: number + quantityScheduled?: number + clientId?: number + clientCode?: string + clientName?: string + vendorId?: number + vendorCode?: string + vendorName?: string + productionDate?: string // 前端字段名,映射到后端 requestDate + requestDate?: string // 后端实际字段名(数据库列 request_date) + finishDate?: string + cancelDate?: string + parentId?: number + ancestors?: string + status?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string + // v3.1 新增字段 + orderDate?: string // 单据日期 + businessType?: string // 业务类型 + businessStatus?: string // 业务状态 + operatorName?: string // 操作员 + approverName?: string // 审核员 + approveDate?: string // 审核日期 + salesOrderCode?: string // 跟单编号(销售订单号) + deliveryDate?: string // 订单交期 + drawingNo?: string // 图纸号 + productionLine?: string // 生产线 + processTime?: number // 加工时长(分钟) + qualityReq?: string // 质量要求 +} + +/** 查询参数 */ +export interface WorkOrderQuery { + workorderCode?: string + workorderName?: string + workorderType?: string + sourceCode?: string + productCode?: string + productName?: string + status?: string + beginProductionDate?: string // 前端用,映射到后端 beginRequestDate + endProductionDate?: string // 前端用,映射到后端 endRequestDate + pageNum?: number + pageSize?: number +} + +/** 分页响应 */ +export interface WorkOrderListResponse { + rows: WorkOrder[] + total: number +} + +// ============ 状态映射 ============ + +export const STATUS_MAP: Record = { + 'PREPARE': { label: '草稿', type: 'info' }, + 'CONFIRMED': { label: '已确认', type: '' }, + 'APPROVED': { label: '已审核', type: 'success' }, + 'FINISHED': { label: '已完成', type: 'success' }, + 'CANCELED': { label: '已取消', type: 'danger' } +} + +export const WORKORDER_TYPE_OPTIONS = [ + { value: 'SELF', label: '自产' }, + { value: 'OUTSOURCE', label: '委外' }, + { value: 'PROCESSING', label: '加工车间' } +] + +export const BUSINESS_STATUS_OPTIONS = [ + { value: 'NORMAL', label: '正常' }, + { value: 'PAUSE', label: '暂停' }, + { value: 'CANCEL', label: '取消' } +] + +// ============ API 接口 ============ + +const BASE = '/mes/pro/workorder' + +/** 查询生产工单列表 */ +export function getWorkOrderList(params: WorkOrderQuery): Promise { + // 将前端日期查询参数映射为后端字段名 + const backendParams: Record = { ...params } + if (backendParams.beginProductionDate) { + backendParams.beginRequestDate = backendParams.beginProductionDate + delete backendParams.beginProductionDate + } + if (backendParams.endProductionDate) { + backendParams.endRequestDate = backendParams.endProductionDate + delete backendParams.endProductionDate + } + return request.get(`${BASE}/list`, { params: backendParams }).then((res: any) => { + const rows = (res.rows ?? res.data?.rows ?? []).map((row: any) => { + // 后端返回 requestDate,前端显示用 productionDate + if (row.requestDate && !row.productionDate) { + row.productionDate = row.requestDate + } + return row + }) + const total = res.total ?? res.data?.total ?? 0 + return { rows, total } + }) +} + +/** 获取生产工单详情 */ +export function getWorkOrderDetail(workorderId: number): Promise { + return request.get(`${BASE}/${workorderId}`).then((res: any) => { + const data = res.data ?? res + // 后端返回 requestDate,前端用 productionDate + if (data.requestDate && !data.productionDate) { + data.productionDate = data.requestDate + } + return data + }) +} + +/** 将前端字段名映射为后端字段名 */ +function toBackendPayload(data: Partial): Record { + const payload: Record = { ...data } + // 前端用 productionDate,后端用 requestDate + if (payload.productionDate !== undefined) { + payload.requestDate = payload.productionDate + delete payload.productionDate + } + return payload +} + +/** 新增生产工单 */ +export function createWorkOrder(data: Partial): Promise { + return request.post(BASE, toBackendPayload(data)).then((res: any) => res.data ?? res) +} + +/** 修改生产工单 */ +export function updateWorkOrder(data: Partial): Promise { + return request.put(BASE, toBackendPayload(data)) +} + +/** 删除生产工单(支持批量) */ +export function deleteWorkOrder(workorderIds: number | number[] | string): Promise { + const ids = Array.isArray(workorderIds) ? workorderIds.join(',') : String(workorderIds) + return request.delete(`${BASE}/${ids}`) +} + +/** 完工工单 */ +export function finishWorkOrder(workorderId: number): Promise { + return request.put(`${BASE}/finish/${workorderId}`) +} + +/** 取消工单 */ +export function cancelWorkOrder(workorderId: number): Promise { + return request.put(`${BASE}/cancel/${workorderId}`) +} + +/** 获取工单 BOM 组成列表(直接查 pro_workorder_bom 表,字段名直接对应) */ +export function getWorkOrderBomList(workorderId: number): Promise { + return request.get('/mes/pro/workorderbom/list', { + params: { workorderId, pageNum: 1, pageSize: 500 } + }).then((res: any) => { + return res.rows ?? res.data?.rows ?? [] + }) +} + +/** 导出生产工单 */ +export function exportWorkOrder(params: WorkOrderQuery): Promise { + return request.post(`${BASE}/export`, params, { responseType: 'blob' }) +} + +/** 一键领料:根据工单 BOM 自动创建领料单,返回领料单 ID */ +export function quickIssue(workorderId: number): Promise { + return request.post(`${BASE}/quickIssue/${workorderId}`).then((res: any) => res.data ?? res) +} + +/** 审核工单(确认):PREPARE → CONFIRMED */ +export function confirmWorkOrder(workorderId: number): Promise { + return request.put(`${BASE}/confirm/${workorderId}`) +} + +/** 反审核工单:CONFIRMED → PREPARE */ +export function unconfirmWorkOrder(workorderId: number): Promise { + return request.put(`${BASE}/unconfirm/${workorderId}`) +} + +/** 查询工单关联的领料明细列表 */ +export function getIssueListByWorkorder(workorderId: number): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/wm/issueheader/list', { + params: { workorderId, pageNum: 1, pageSize: 100 } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +// ============ 工序任务(ProTask)API ============ + +/** 工序任务 */ +export interface ProTask { + taskId?: number + taskCode?: string + taskName?: string + workorderId?: number + workorderCode?: string + processId?: number + processCode?: string + processName?: string + quantity?: number + quantityProduced?: number + quantityQuanlified?: number + quantityUnquanlified?: number + startTime?: string + duration?: number + endTime?: string + colorCode?: string + status?: string + remark?: string + createBy?: string + createTime?: string + updateBy?: string + updateTime?: string +} + +/** 根据工单 ID 获取工序任务列表 */ +export function getTaskListByWorkorder(workorderId: number): Promise { + return request.get('/mes/pro/protask/list', { + params: { workorderId, pageNum: 1, pageSize: 500 } + }).then((res: any) => { + return res.rows ?? res.data?.rows ?? [] + }) +} + +// ============ 工艺路线选择 API ============ + +/** 工艺路线列表(选择弹窗,不默认过滤 enableFlag 以避免列表为空) */ +export function getRouteList(params?: { + routeCode?: string + routeName?: string + enableFlag?: string + pageNum?: number + pageSize?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/pro/proroute/list', { + params: { pageNum: 1, pageSize: 100, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} + +// ============ 客户选择 API(备用) ============ + +/** 客户列表(选择弹窗) */ +export function getClientList(params?: { + clientCode?: string + clientName?: string + pageNum?: number + pageSize?: number +}): Promise<{ rows: any[]; total: number }> { + return request.get('/mes/md/client/list', { + params: { pageNum: 1, pageSize: 100, ...params } + }).then((res: any) => ({ + rows: res.rows ?? [], + total: res.total ?? 0 + })) +} diff --git a/erp-frontend-vue/src/assets/logo-color.png b/erp-frontend-vue/src/assets/logo-color.png new file mode 100644 index 0000000..22c691b Binary files /dev/null and b/erp-frontend-vue/src/assets/logo-color.png differ diff --git a/erp-frontend-vue/src/assets/logo-white.png b/erp-frontend-vue/src/assets/logo-white.png new file mode 100644 index 0000000..500a153 Binary files /dev/null and b/erp-frontend-vue/src/assets/logo-white.png differ diff --git a/erp-frontend-vue/src/assets/vue.svg b/erp-frontend-vue/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/erp-frontend-vue/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/erp-frontend-vue/src/components/HelloWorld.vue b/erp-frontend-vue/src/components/HelloWorld.vue new file mode 100644 index 0000000..b58e52b --- /dev/null +++ b/erp-frontend-vue/src/components/HelloWorld.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/erp-frontend-vue/src/components/print/PrintDialog.vue b/erp-frontend-vue/src/components/print/PrintDialog.vue new file mode 100644 index 0000000..7fb9fb1 --- /dev/null +++ b/erp-frontend-vue/src/components/print/PrintDialog.vue @@ -0,0 +1,309 @@ + + + + + + + diff --git a/erp-frontend-vue/src/components/print/QrCode.vue b/erp-frontend-vue/src/components/print/QrCode.vue new file mode 100644 index 0000000..394bbcb --- /dev/null +++ b/erp-frontend-vue/src/components/print/QrCode.vue @@ -0,0 +1,38 @@ + + + diff --git a/erp-frontend-vue/src/components/print/types.ts b/erp-frontend-vue/src/components/print/types.ts new file mode 100644 index 0000000..ebe9223 --- /dev/null +++ b/erp-frontend-vue/src/components/print/types.ts @@ -0,0 +1,39 @@ +/** 表头字段 */ +export interface PrintHeaderField { + label: string // "单据编码" + value: string | number // "SCDD000001" + span?: number // 占列数,默认 1(共 4 列一行) +} + +/** 明细表列 */ +export interface PrintColumn { + prop: string // 数据字段名 + label: string // 列标题 + width?: string // 列宽 + align?: 'left' | 'center' | 'right' + type?: 'text' | 'index' | 'qrcode' | 'amount' + qrCodeProp?: string // type=qrcode 时的编码内容字段 + formatter?: (row: any, index: number) => string +} + +/** 合计行 */ +export interface PrintSummary { + label: string + value: string | number +} + +/** 完整打印配置 */ +export interface PrintConfig { + systemName?: string // 默认 "升阳云ERP" + title: string // "销售订单" | "到货通知单" | ... + subtitle?: string // "加工车间" 等 + qrCodeValue?: string // 右上角二维码内容 + headerFields: PrintHeaderField[] // 4 列网格,自动换行 + columns: PrintColumn[] // 明细表列 + data: any[] // 明细表数据 + summaries?: PrintSummary[] // 合计行(可选) + footer: { + creator?: string // 制单人 + approver?: string // 审核人 + } +} diff --git a/erp-frontend-vue/src/layout/index.vue b/erp-frontend-vue/src/layout/index.vue new file mode 100644 index 0000000..0aa90e2 --- /dev/null +++ b/erp-frontend-vue/src/layout/index.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/erp-frontend-vue/src/main.ts b/erp-frontend-vue/src/main.ts new file mode 100644 index 0000000..38990ea --- /dev/null +++ b/erp-frontend-vue/src/main.ts @@ -0,0 +1,23 @@ +import { createApp } from 'vue' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +// @ts-ignore +import zhCn from 'element-plus/dist/locale/zh-cn.mjs' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import router from './router' +import App from './App.vue' +import './style.css' + +// 路由守卫(需要在 router 使用之后引入) +import './permission' + +const app = createApp(App) + +// 注册所有 Element Plus 图标 +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +app.use(ElementPlus, { locale: zhCn }) +app.use(router) +app.mount('#app') diff --git a/erp-frontend-vue/src/permission.ts b/erp-frontend-vue/src/permission.ts new file mode 100644 index 0000000..a08fb86 --- /dev/null +++ b/erp-frontend-vue/src/permission.ts @@ -0,0 +1,70 @@ +import router from './router' +import { ElMessage } from 'element-plus' +import { getToken } from '@/utils/auth' +import { useUserStore } from '@/stores/user' + +// 白名单路由(不需要登录即可访问) +const whiteList = ['/login', '/register'] + +// DEV模式:后端未运行时跳过认证,允许直接访问页面 +const DEV_SKIP_AUTH = import.meta.env.DEV && !import.meta.env.VITE_REQUIRE_AUTH + +// 路由守卫 +router.beforeEach(async (to, from, next) => { + // 设置页面标题 + document.title = to.meta.title ? `${to.meta.title} - ERP系统` : 'ERP系统' + + // DEV模式跳过认证:设置模拟角色以通过权限检查 + if (DEV_SKIP_AUTH) { + const userStore = useUserStore() + if (userStore.state.userInfo.roles.length === 0) { + userStore.state.userInfo.roles = ['admin'] + userStore.state.userInfo.permissions = ['*:*:*'] + userStore.state.userInfo.userName = 'dev' + userStore.state.userInfo.nickName = '开发模式' + } + next() + return + } + + const token = getToken() + const userStore = useUserStore() + + if (token) { + // 已登录 + if (to.path === '/login') { + // 已登录状态下访问登录页,重定向到首页 + next({ path: '/' }) + } else { + // 检查是否已获取用户信息 + if (userStore.state.userInfo.roles.length === 0) { + try { + // 获取用户信息 + await userStore.getUserInfo() + // 确保路由完成后再跳转 + next({ ...to, replace: true }) + } catch (error: any) { + // 获取用户信息失败,可能是 token 过期 + await userStore.fedLogout() + ElMessage.error(error?.message || '获取用户信息失败,请重新登录') + next(`/login?redirect=${to.fullPath}`) + } + } else { + next() + } + } + } else { + // 未登录 + if (whiteList.includes(to.path)) { + // 在白名单中,直接进入 + next() + } else { + // 不在白名单中,重定向到登录页 + next(`/login?redirect=${to.fullPath}`) + } + } +}) + +router.afterEach(() => { + // 可以在这里添加 NProgress 完成等逻辑 +}) diff --git a/erp-frontend-vue/src/router/index.ts b/erp-frontend-vue/src/router/index.ts new file mode 100644 index 0000000..028fba3 --- /dev/null +++ b/erp-frontend-vue/src/router/index.ts @@ -0,0 +1,538 @@ +import { createRouter, createWebHistory } from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' +import Layout from '@/layout/index.vue' + +const routes: RouteRecordRaw[] = [ + { + path: '/login', + name: 'Login', + component: () => import('@/views/Login.vue'), + meta: { title: '登录' } + }, + { + path: '/', + component: Layout, + redirect: '/dashboard', + children: [ + { + path: 'dashboard', + name: 'Dashboard', + component: () => import('@/views/Dashboard.vue'), + meta: { title: '首页' } + } + ] + }, + { + path: '/sales', + component: Layout, + redirect: '/sales/customer', + meta: { title: '销售管理' }, + children: [ + { + path: 'customer', + name: 'CustomerList', + component: () => import('@/views/Sales/Customer/index.vue'), + meta: { title: '客户档案' } + }, + { + path: 'contract', + name: 'ContractList', + component: () => import('@/views/Sales/Contract/index.vue'), + meta: { title: '销售合同' } + }, + { + path: 'contract/new', + name: 'ContractNew', + component: () => import('@/views/Sales/Contract/form.vue'), + meta: { title: '新增销售合同' } + }, + { + path: 'contract/view/:id', + name: 'ContractView', + component: () => import('@/views/Sales/Contract/form.vue'), + meta: { title: '合同详情' } + }, + { + path: 'order', + name: 'SalesOrderList', + component: () => import('@/views/Sales/Order/index.vue'), + meta: { title: '销售订单' } + }, + { + path: 'order/form', + name: 'SalesOrderNew', + component: () => import('@/views/Sales/Order/form.vue'), + meta: { title: '新增销售订单' } + }, + { + path: 'order/form/:id', + name: 'SalesOrderEdit', + component: () => import('@/views/Sales/Order/form.vue'), + meta: { title: '编辑销售订单' } + }, + { + path: 'order/view/:id', + name: 'SalesOrderView', + component: () => import('@/views/Sales/Order/view.vue'), + meta: { title: '销售订单详情' } + }, + { + path: 'order/progress/:id', + name: 'SalesOrderProgress', + component: () => import('@/views/Sales/Order/index.vue'), + meta: { title: '生产进度' } + }, + { + path: 'hold-order', + name: 'HoldOrderList', + component: () => import('@/views/Sales/Order/index.vue'), + meta: { title: '备货订单' } + }, + { + path: 'deliver', + name: 'DeliverList', + component: () => import('@/views/Sales/Deliver/index.vue'), + meta: { title: '发货通知单' } + }, + { + path: 'deliver/new', + name: 'DeliverNew', + component: () => import('@/views/Sales/Deliver/form.vue'), + meta: { title: '新增发货通知单' } + }, + { + path: 'invoice', + name: 'InvoiceList', + component: () => import('@/views/Sales/Invoice/index.vue'), + meta: { title: '开票结算单' } + }, + { + path: 'invoice/new', + name: 'InvoiceNew', + component: () => import('@/views/Sales/Invoice/form.vue'), + meta: { title: '新增开票结算单' } + }, + { + path: 'saleback', + name: 'SalebackList', + component: () => import('@/views/Sales/Saleback/index.vue'), + meta: { title: '退货通知单' } + }, + { + path: 'saleback/new', + name: 'SalebackNew', + component: () => import('@/views/Sales/Saleback/form.vue'), + meta: { title: '新增退货通知单' } + }, + { + path: 'plan', + name: 'PlanReport', + component: () => import('@/views/Sales/Reports/PlanReport.vue'), + meta: { title: '销售订单计划表' } + }, + { + path: 'detail', + name: 'DetailReport', + component: () => import('@/views/Sales/Reports/DetailReport.vue'), + meta: { title: '销售订单明细表' } + }, + { + path: 'seven-day', + name: 'SevenDayReport', + component: () => import('@/views/Sales/Reports/SevenDayReport.vue'), + meta: { title: '7天交货明细表' } + } + ] + }, + { + path: '/purchasing', + component: Layout, + redirect: '/purchasing/supplier', + meta: { title: '采购管理' }, + children: [ + { + path: 'supplier', + name: 'SupplierList', + component: () => import('@/views/Purchasing/Supplier/index.vue'), + meta: { title: '供应商' } + }, + { + path: 'order', + name: 'PurchaseOrderList', + component: () => import('@/views/Purchasing/Order/index.vue'), + meta: { title: '采购订单' } + }, + { + path: 'order/new', + name: 'PurchaseOrderNew', + component: () => import('@/views/Purchasing/Order/form.vue'), + meta: { title: '新增采购订单' } + }, + { + path: 'order/edit/:id', + name: 'PurchaseOrderEdit', + component: () => import('@/views/Purchasing/Order/form.vue'), + meta: { title: '编辑采购订单' } + }, + { + path: 'order/view/:id', + name: 'PurchaseOrderView', + component: () => import('@/views/Purchasing/Order/form.vue'), + meta: { title: '采购订单详情' } + }, + { + path: 'stock-order', + name: 'StockOrderList', + component: () => import('@/views/Purchasing/Order/index.vue'), + meta: { title: '备库订单' } + }, + { + path: 'checkin', + name: 'CheckinList', + component: () => import('@/views/Purchasing/Checkin/index.vue'), + meta: { title: '采购到货单' } + }, + { + path: 'invoice', + name: 'PurchaseInvoiceList', + component: () => import('@/views/Purchasing/Invoice/index.vue'), + meta: { title: '采购发票' } + }, + { + path: 'reject', + name: 'RejectList', + component: () => import('@/views/Purchasing/Reject/index.vue'), + meta: { title: '采购退货单' } + }, + { + path: 'report/detail', + name: 'PoDetailReport', + component: () => import('@/views/Purchasing/Reports/DetailReport.vue'), + meta: { title: '采购执行明细表' } + }, + { + path: 'report/total', + name: 'PoTotalReport', + component: () => import('@/views/Purchasing/Reports/TotalReport.vue'), + meta: { title: '采购执行汇总表' } + }, + { + path: 'report/checkin', + name: 'PoCheckinReport', + component: () => import('@/views/Purchasing/Reports/CheckinReport.vue'), + meta: { title: '采购到货明细表' } + }, + { + path: 'report/fapiao', + name: 'PoInvoiceReport', + component: () => import('@/views/Purchasing/Reports/InvoiceReport.vue'), + meta: { title: '采购发票明细表' } + } + ] + }, + { + path: '/production', + component: Layout, + redirect: '/production/plan-order', + meta: { title: '生产计划' }, + children: [ + { + path: 'plan-order', + name: 'ProductionPlanList', + component: () => import('@/views/Production/PlanOrder/index.vue'), + meta: { title: '生产计划单' } + }, + { + path: 'plan-order/new', + name: 'ProductionPlanNew', + component: () => import('@/views/Production/PlanOrder/form.vue'), + meta: { title: '新增生产计划单' } + }, + { + path: 'plan-order/edit/:id', + name: 'ProductionPlanEdit', + component: () => import('@/views/Production/PlanOrder/form.vue'), + meta: { title: '编辑生产计划单' } + }, + { + path: 'plan-order/view/:id', + name: 'ProductionPlanView', + component: () => import('@/views/Production/PlanOrder/form.vue'), + meta: { title: '生产计划单详情' } + }, + { + path: 'work-order', + name: 'WorkOrderList', + component: () => import('@/views/Production/WorkOrder/index.vue'), + meta: { title: '生产订单' } + }, + { + path: 'work-order/new', + name: 'WorkOrderNew', + component: () => import('@/views/Production/WorkOrder/form.vue'), + meta: { title: '新增生产工单' } + }, + { + path: 'work-order/edit/:id', + name: 'WorkOrderEdit', + component: () => import('@/views/Production/WorkOrder/form.vue'), + meta: { title: '编辑生产工单' } + }, + { + path: 'work-order/view/:id', + name: 'WorkOrderView', + component: () => import('@/views/Production/WorkOrder/form.vue'), + meta: { title: '查看生产工单' } + }, + { + path: 'parts', + name: 'PartsOrderList', + component: () => import('@/views/Production/Parts/index.vue'), + meta: { title: '零部件订单' } + }, + { + path: 'mbom', + name: 'MbomList', + component: () => import('@/views/Production/Mbom/index.vue'), + meta: { title: '物料清单' } + }, + { + path: 'mbom/view/:id', + name: 'MbomView', + component: () => import('@/views/Production/Mbom/form.vue'), + meta: { title: '查看物料清单' } + }, + { + path: 'mbom/edit/:id', + name: 'MbomEdit', + component: () => import('@/views/Production/Mbom/form.vue'), + meta: { title: '编辑物料清单' } + }, + { + path: 'purchase-plan', + name: 'PurchasePlanList', + component: () => import('@/views/Production/PurchasePlan/index.vue'), + meta: { title: '采购计划单' } + }, + { + path: 'purchase-plan/new', + name: 'PurchasePlanNew', + component: () => import('@/views/Production/PurchasePlan/form.vue'), + meta: { title: '新增采购计划' } + }, + { + path: 'purchase-plan/edit/:id', + name: 'PurchasePlanEdit', + component: () => import('@/views/Production/PurchasePlan/form.vue'), + meta: { title: '编辑采购计划' } + }, + { + path: 'purchase-plan/view/:id', + name: 'PurchasePlanView', + component: () => import('@/views/Production/PurchasePlan/form.vue'), + meta: { title: '查看采购计划' } + }, + { + path: 'stock-plan', + name: 'StockPlanList', + component: () => import('@/views/Production/PurchasePlan/index.vue'), + meta: { title: '备库计划单' } + }, + { + path: 'report/detail', + name: 'MpDetailReport', + component: () => import('@/views/Production/Reports/DetailReport.vue'), + meta: { title: '计划执行明细表' } + }, + { + path: 'report/total', + name: 'MpTotalReport', + component: () => import('@/views/Production/Reports/TotalReport.vue'), + meta: { title: '计划执行汇总表' } + }, + { + path: 'report/need', + name: 'PurchaseNeedReport', + component: () => import('@/views/Production/Reports/NeedReport.vue'), + meta: { title: '采购计划需求表' } + } + ] + }, + { + path: '/system', + component: Layout, + redirect: '/system/user', + meta: { title: '组织架构' }, + children: [ + { + path: 'user', + name: 'UserList', + component: () => import('@/views/System/User/index.vue'), + meta: { title: '用户管理' } + }, + { + path: 'role', + name: 'RoleList', + component: () => import('@/views/System/Role/index.vue'), + meta: { title: '角色管理' } + }, + { + path: 'dept', + name: 'DeptList', + component: () => import('@/views/System/Dept/index.vue'), + meta: { title: '部门管理' } + }, + { + path: 'post', + name: 'PostList', + component: () => import('@/views/System/Post/index.vue'), + meta: { title: '岗位管理' } + }, + { + path: 'user-auth/role/:userId', + name: 'UserAuthRole', + component: () => import('@/views/System/User/index.vue'), + meta: { title: '分配角色' } + }, + { + path: 'role-auth/user/:roleId', + name: 'RoleAuthUser', + component: () => import('@/views/System/Role/index.vue'), + meta: { title: '分配用户' } + } + ] + }, + { + path: '/rd', + component: Layout, + redirect: '/rd/ebom', + meta: { title: '研发管理' }, + children: [ + { + path: 'ebom', + name: 'EbomList', + component: () => import('@/views/RD/Ebom/index.vue'), + meta: { title: '产品BOM' } + }, + { + path: 'ebom/new', + name: 'EbomNew', + component: () => import('@/views/RD/Ebom/form.vue'), + meta: { title: '新增EBOM' } + }, + { + path: 'ebom/edit/:id', + name: 'EbomEdit', + component: () => import('@/views/RD/Ebom/form.vue'), + meta: { title: '编辑EBOM' } + }, + { + path: 'ebom/view/:id', + name: 'EbomView', + component: () => import('@/views/RD/Ebom/form.vue'), + meta: { title: 'EBOM详情' } + } + ] + }, + { + path: '/masterdata', + component: Layout, + redirect: '/masterdata/item', + meta: { title: '主数据' }, + children: [ + { + path: 'item', + name: 'MdItemList', + component: () => import('@/views/MasterData/Item/index.vue'), + meta: { title: '物料管理' } + }, + { + path: 'itemtype', + name: 'ItemTypeList', + component: () => import('@/views/MasterData/ItemType/index.vue'), + meta: { title: '物料分类' } + }, + { + path: 'unit', + name: 'UnitList', + component: () => import('@/views/MasterData/Unit/index.vue'), + meta: { title: '计量单位' } + }, + { + path: 'workshop', + name: 'WorkshopList', + component: () => import('@/views/MasterData/Workshop/index.vue'), + meta: { title: '车间管理' } + }, + { + path: 'workstation', + name: 'WorkstationList', + component: () => import('@/views/MasterData/Workstation/index.vue'), + meta: { title: '工作站管理' } + }, + { + path: 'bom', + name: 'BomList', + component: () => import('@/views/MasterData/Bom/list.vue'), + meta: { title: '产品BOM' } + }, + { + path: 'bom/new', + name: 'BomNew', + component: () => import('@/views/MasterData/Bom/form.vue'), + meta: { title: '新增产品BOM' } + }, + { + path: 'bom/edit/:id', + name: 'BomEdit', + component: () => import('@/views/MasterData/Bom/form.vue'), + meta: { title: '编辑产品BOM' } + }, + { + path: 'bom/view/:id', + name: 'BomView', + component: () => import('@/views/MasterData/Bom/form.vue'), + meta: { title: '产品BOM详情' } + } + ] + }, + { + path: '/warehouse', + component: Layout, + redirect: '/warehouse/issue', + meta: { title: '仓储管理' }, + children: [ + { + path: 'issue', + name: 'IssueList', + component: () => import('@/views/Warehouse/Issue/index.vue'), + meta: { title: '生产领料' } + }, + { + path: 'issue/new', + name: 'IssueNew', + component: () => import('@/views/Warehouse/Issue/form.vue'), + meta: { title: '新增领料单' } + }, + { + path: 'issue/edit/:id', + name: 'IssueEdit', + component: () => import('@/views/Warehouse/Issue/form.vue'), + meta: { title: '编辑领料单' } + }, + { + path: 'issue/view/:id', + name: 'IssueView', + component: () => import('@/views/Warehouse/Issue/form.vue'), + meta: { title: '查看领料单' } + } + ] + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +export default router diff --git a/erp-frontend-vue/src/stores/user.ts b/erp-frontend-vue/src/stores/user.ts new file mode 100644 index 0000000..05a636f --- /dev/null +++ b/erp-frontend-vue/src/stores/user.ts @@ -0,0 +1,111 @@ +import { reactive } from 'vue' +import { login, logout, getInfo } from '@/api/auth' +import { getToken, setToken, removeToken } from '@/utils/auth' + +export interface UserInfo { + userId: number | null + userName: string + nickName: string + avatar: string + roles: string[] + permissions: string[] +} + +export interface UserState { + token: string | null + userInfo: UserInfo + isLoggedIn: boolean +} + +const state = reactive({ + token: getToken(), + userInfo: { + userId: null, + userName: '', + nickName: '', + avatar: '', + roles: [], + permissions: [] + }, + isLoggedIn: !!getToken() +}) + +// 登录 +async function loginAction(username: string, password: string, code: string, uuid: string) { + const trimmedUsername = username.trim() + const res = await login(trimmedUsername, password, code, uuid) + if (res.token) { + setToken(res.token) + state.token = res.token + state.isLoggedIn = true + } + return res +} + +// 获取用户信息 +async function getUserInfo() { + const res = await getInfo() + const user = res.user + if (res.roles && res.roles.length > 0) { + state.userInfo.roles = res.roles + state.userInfo.permissions = res.permissions || [] + } else { + state.userInfo.roles = ['ROLE_DEFAULT'] + } + state.userInfo.userId = user.userId + state.userInfo.userName = user.userName + state.userInfo.nickName = user.nickName + state.userInfo.avatar = user.avatar || '' + return res +} + +// 退出登录 +async function logoutAction() { + try { + await logout() + } catch (e) { + // 忽略退出接口错误 + } + resetState() +} + +// 前端登出(不调用后端接口) +function fedLogout() { + resetState() +} + +// 重置状态 +function resetState() { + removeToken() + state.token = null + state.isLoggedIn = false + state.userInfo = { + userId: null, + userName: '', + nickName: '', + avatar: '', + roles: [], + permissions: [] + } +} + +// 检查是否有权限 +function hasRole(role: string): boolean { + return state.userInfo.roles.includes(role) || state.userInfo.roles.includes('admin') +} + +function hasPermission(permission: string): boolean { + return state.userInfo.permissions.includes('*:*:*') || + state.userInfo.permissions.includes(permission) +} + +export const useUserStore = () => ({ + state, + loginAction, + getUserInfo, + logoutAction, + fedLogout, + resetState, + hasRole, + hasPermission +}) diff --git a/erp-frontend-vue/src/style.css b/erp-frontend-vue/src/style.css new file mode 100644 index 0000000..d652c79 --- /dev/null +++ b/erp-frontend-vue/src/style.css @@ -0,0 +1,41 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, body, #app { + width: 100%; + height: 100%; + font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', SimSun, sans-serif; +} + +/* 自定义滚动条 */ +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-thumb { + background-color: #c0c4cc; + border-radius: 3px; +} + +::-webkit-scrollbar-track { + background-color: #f5f7fa; +} + +/* 工具栏按钮样式 */ +.toolbar-btn { + margin-right: 8px; +} + +/* 表单间距 */ +.el-form-item { + margin-bottom: 18px; +} + +/* 表格样式优化 */ +.el-table th.el-table__cell { + background-color: #f5f7fa; +} diff --git a/erp-frontend-vue/src/utils/auth.ts b/erp-frontend-vue/src/utils/auth.ts new file mode 100644 index 0000000..0221ecd --- /dev/null +++ b/erp-frontend-vue/src/utils/auth.ts @@ -0,0 +1,13 @@ +const TokenKey = 'Admin-Token' + +export function getToken(): string | null { + return localStorage.getItem(TokenKey) +} + +export function setToken(token: string): void { + localStorage.setItem(TokenKey, token) +} + +export function removeToken(): void { + localStorage.removeItem(TokenKey) +} diff --git a/erp-frontend-vue/src/views/Dashboard.vue b/erp-frontend-vue/src/views/Dashboard.vue new file mode 100644 index 0000000..199a757 --- /dev/null +++ b/erp-frontend-vue/src/views/Dashboard.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Login.vue b/erp-frontend-vue/src/views/Login.vue new file mode 100644 index 0000000..494b08e --- /dev/null +++ b/erp-frontend-vue/src/views/Login.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/Bom/form.vue b/erp-frontend-vue/src/views/MasterData/Bom/form.vue new file mode 100644 index 0000000..b7d55ed --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/Bom/form.vue @@ -0,0 +1,656 @@ + + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/Bom/list.vue b/erp-frontend-vue/src/views/MasterData/Bom/list.vue new file mode 100644 index 0000000..af85c10 --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/Bom/list.vue @@ -0,0 +1,436 @@ + + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/Item/index.vue b/erp-frontend-vue/src/views/MasterData/Item/index.vue new file mode 100644 index 0000000..2bfb907 --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/Item/index.vue @@ -0,0 +1,525 @@ + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/ItemType/index.vue b/erp-frontend-vue/src/views/MasterData/ItemType/index.vue new file mode 100644 index 0000000..8330a1e --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/ItemType/index.vue @@ -0,0 +1,285 @@ + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/Unit/index.vue b/erp-frontend-vue/src/views/MasterData/Unit/index.vue new file mode 100644 index 0000000..15e9e49 --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/Unit/index.vue @@ -0,0 +1,293 @@ + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/Workshop/index.vue b/erp-frontend-vue/src/views/MasterData/Workshop/index.vue new file mode 100644 index 0000000..93806bf --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/Workshop/index.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/erp-frontend-vue/src/views/MasterData/Workstation/index.vue b/erp-frontend-vue/src/views/MasterData/Workstation/index.vue new file mode 100644 index 0000000..06d47af --- /dev/null +++ b/erp-frontend-vue/src/views/MasterData/Workstation/index.vue @@ -0,0 +1,305 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/Mbom/form.vue b/erp-frontend-vue/src/views/Production/Mbom/form.vue new file mode 100644 index 0000000..bfcd92a --- /dev/null +++ b/erp-frontend-vue/src/views/Production/Mbom/form.vue @@ -0,0 +1,358 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/Mbom/index.vue b/erp-frontend-vue/src/views/Production/Mbom/index.vue new file mode 100644 index 0000000..07b9a3c --- /dev/null +++ b/erp-frontend-vue/src/views/Production/Mbom/index.vue @@ -0,0 +1,518 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/Parts/index.vue b/erp-frontend-vue/src/views/Production/Parts/index.vue new file mode 100644 index 0000000..9b2bb56 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/Parts/index.vue @@ -0,0 +1,372 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/PlanOrder/form.vue b/erp-frontend-vue/src/views/Production/PlanOrder/form.vue new file mode 100644 index 0000000..e6c8681 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/PlanOrder/form.vue @@ -0,0 +1,1557 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/PlanOrder/index.vue b/erp-frontend-vue/src/views/Production/PlanOrder/index.vue new file mode 100644 index 0000000..8b7fe66 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/PlanOrder/index.vue @@ -0,0 +1,766 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/PurchasePlan/form.vue b/erp-frontend-vue/src/views/Production/PurchasePlan/form.vue new file mode 100644 index 0000000..5ca2c20 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/PurchasePlan/form.vue @@ -0,0 +1,1006 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/PurchasePlan/index.vue b/erp-frontend-vue/src/views/Production/PurchasePlan/index.vue new file mode 100644 index 0000000..358dc30 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/PurchasePlan/index.vue @@ -0,0 +1,763 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/Reports/DetailReport.vue b/erp-frontend-vue/src/views/Production/Reports/DetailReport.vue new file mode 100644 index 0000000..2696077 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/Reports/DetailReport.vue @@ -0,0 +1,360 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/Reports/NeedReport.vue b/erp-frontend-vue/src/views/Production/Reports/NeedReport.vue new file mode 100644 index 0000000..eec4f79 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/Reports/NeedReport.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/Reports/TotalReport.vue b/erp-frontend-vue/src/views/Production/Reports/TotalReport.vue new file mode 100644 index 0000000..0e520e0 --- /dev/null +++ b/erp-frontend-vue/src/views/Production/Reports/TotalReport.vue @@ -0,0 +1,341 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/WorkOrder/form.vue b/erp-frontend-vue/src/views/Production/WorkOrder/form.vue new file mode 100644 index 0000000..2744c8a --- /dev/null +++ b/erp-frontend-vue/src/views/Production/WorkOrder/form.vue @@ -0,0 +1,861 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Production/WorkOrder/index.vue b/erp-frontend-vue/src/views/Production/WorkOrder/index.vue new file mode 100644 index 0000000..5981a1d --- /dev/null +++ b/erp-frontend-vue/src/views/Production/WorkOrder/index.vue @@ -0,0 +1,546 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Checkin/index.vue b/erp-frontend-vue/src/views/Purchasing/Checkin/index.vue new file mode 100644 index 0000000..6e0388e --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Checkin/index.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Invoice/index.vue b/erp-frontend-vue/src/views/Purchasing/Invoice/index.vue new file mode 100644 index 0000000..542c730 --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Invoice/index.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Order/form.vue b/erp-frontend-vue/src/views/Purchasing/Order/form.vue new file mode 100644 index 0000000..56684ce --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Order/form.vue @@ -0,0 +1,949 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Order/index.vue b/erp-frontend-vue/src/views/Purchasing/Order/index.vue new file mode 100644 index 0000000..6445ddd --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Order/index.vue @@ -0,0 +1,337 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Reject/index.vue b/erp-frontend-vue/src/views/Purchasing/Reject/index.vue new file mode 100644 index 0000000..42ea4b3 --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Reject/index.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Reports/CheckinReport.vue b/erp-frontend-vue/src/views/Purchasing/Reports/CheckinReport.vue new file mode 100644 index 0000000..04aa9c5 --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Reports/CheckinReport.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Reports/DetailReport.vue b/erp-frontend-vue/src/views/Purchasing/Reports/DetailReport.vue new file mode 100644 index 0000000..cd3c403 --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Reports/DetailReport.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Reports/InvoiceReport.vue b/erp-frontend-vue/src/views/Purchasing/Reports/InvoiceReport.vue new file mode 100644 index 0000000..a4f662a --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Reports/InvoiceReport.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Reports/TotalReport.vue b/erp-frontend-vue/src/views/Purchasing/Reports/TotalReport.vue new file mode 100644 index 0000000..074523f --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Reports/TotalReport.vue @@ -0,0 +1,96 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Purchasing/Supplier/index.vue b/erp-frontend-vue/src/views/Purchasing/Supplier/index.vue new file mode 100644 index 0000000..2951f37 --- /dev/null +++ b/erp-frontend-vue/src/views/Purchasing/Supplier/index.vue @@ -0,0 +1,799 @@ + + + + + diff --git a/erp-frontend-vue/src/views/RD/Ebom/form.vue b/erp-frontend-vue/src/views/RD/Ebom/form.vue new file mode 100644 index 0000000..9258a41 --- /dev/null +++ b/erp-frontend-vue/src/views/RD/Ebom/form.vue @@ -0,0 +1,963 @@ + + + + + diff --git a/erp-frontend-vue/src/views/RD/Ebom/index.vue b/erp-frontend-vue/src/views/RD/Ebom/index.vue new file mode 100644 index 0000000..3736f2b --- /dev/null +++ b/erp-frontend-vue/src/views/RD/Ebom/index.vue @@ -0,0 +1,661 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Contract/form.vue b/erp-frontend-vue/src/views/Sales/Contract/form.vue new file mode 100644 index 0000000..8803323 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Contract/form.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Contract/index.vue b/erp-frontend-vue/src/views/Sales/Contract/index.vue new file mode 100644 index 0000000..c2889d0 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Contract/index.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Customer/__tests__/index.spec.ts b/erp-frontend-vue/src/views/Sales/Customer/__tests__/index.spec.ts new file mode 100644 index 0000000..148eae9 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Customer/__tests__/index.spec.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { mount } from '@vue/test-utils' +import ElementPlus from 'element-plus' +import Customer from '../index.vue' +import * as customerApi from '@/api/customer' + +vi.mock('@/api/customer', () => ({ + getCustomerList: vi.fn(), + getCustomerDetail: vi.fn(), + createCustomer: vi.fn(), + updateCustomer: vi.fn(), + deleteCustomer: vi.fn(), + updateCustomerStatus: vi.fn() +})) + +describe('Customer 客户档案', () => { + beforeEach(() => { + vi.clearAllMocks() + vi.mocked(customerApi.getCustomerList).mockResolvedValue({ list: [], total: 0 }) + }) + + it('renders and loads list', async () => { + vi.mocked(customerApi.getCustomerList).mockResolvedValue({ + list: [ + { + clientId: 1, + clientCode: 'C001', + clientName: '客户A', + clientNick: 'A', + contact1: '张三', + contact1Tel: '13800001111', + address: '深圳', + enableFlag: 'Y', + createTime: '2026-01-25 10:00:00' + } + ], + total: 1 + }) + + const wrapper = mount(Customer, { + global: { + plugins: [ElementPlus], + stubs: { + ElPopconfirm: { template: '
' } + } + } + }) + + await vi.waitFor(() => { + expect(customerApi.getCustomerList).toHaveBeenCalled() + }) + + expect(wrapper.find('.search-card').exists()).toBe(true) + expect(wrapper.find('.table-card').exists()).toBe(true) + expect(wrapper.find('table').exists()).toBe(true) + expect(wrapper.find('.pagination-wrapper').exists()).toBe(true) + }) + + it('has query inputs and toolbar buttons', async () => { + const wrapper = mount(Customer, { + global: { + plugins: [ElementPlus], + stubs: { + ElPopconfirm: { template: '
' } + } + } + }) + + await wrapper.vm.$nextTick() + expect(wrapper.find('.search-card').exists()).toBe(true) + expect(wrapper.text()).toMatch(/新增|刷新|导出|批量删除/) + }) +}) diff --git a/erp-frontend-vue/src/views/Sales/Customer/index.vue b/erp-frontend-vue/src/views/Sales/Customer/index.vue new file mode 100644 index 0000000..734ec7c --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Customer/index.vue @@ -0,0 +1,514 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Deliver/form.vue b/erp-frontend-vue/src/views/Sales/Deliver/form.vue new file mode 100644 index 0000000..d4fdc08 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Deliver/form.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Deliver/index.vue b/erp-frontend-vue/src/views/Sales/Deliver/index.vue new file mode 100644 index 0000000..7385650 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Deliver/index.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Invoice/form.vue b/erp-frontend-vue/src/views/Sales/Invoice/form.vue new file mode 100644 index 0000000..c05ac3e --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Invoice/form.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Invoice/index.vue b/erp-frontend-vue/src/views/Sales/Invoice/index.vue new file mode 100644 index 0000000..f8003a6 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Invoice/index.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Order/form.vue b/erp-frontend-vue/src/views/Sales/Order/form.vue new file mode 100644 index 0000000..e717e9e --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Order/form.vue @@ -0,0 +1,1324 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Order/index.vue b/erp-frontend-vue/src/views/Sales/Order/index.vue new file mode 100644 index 0000000..67b93ca --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Order/index.vue @@ -0,0 +1,629 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Order/view.vue b/erp-frontend-vue/src/views/Sales/Order/view.vue new file mode 100644 index 0000000..4853d5e --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Order/view.vue @@ -0,0 +1,635 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Reports/DetailReport.vue b/erp-frontend-vue/src/views/Sales/Reports/DetailReport.vue new file mode 100644 index 0000000..76a9ce6 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Reports/DetailReport.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Reports/PlanReport.vue b/erp-frontend-vue/src/views/Sales/Reports/PlanReport.vue new file mode 100644 index 0000000..81d84b4 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Reports/PlanReport.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Reports/SevenDayReport.vue b/erp-frontend-vue/src/views/Sales/Reports/SevenDayReport.vue new file mode 100644 index 0000000..84e5544 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Reports/SevenDayReport.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Saleback/form.vue b/erp-frontend-vue/src/views/Sales/Saleback/form.vue new file mode 100644 index 0000000..df7085b --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Saleback/form.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Sales/Saleback/index.vue b/erp-frontend-vue/src/views/Sales/Saleback/index.vue new file mode 100644 index 0000000..9ab7729 --- /dev/null +++ b/erp-frontend-vue/src/views/Sales/Saleback/index.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/erp-frontend-vue/src/views/System/Dept/index.vue b/erp-frontend-vue/src/views/System/Dept/index.vue new file mode 100644 index 0000000..5e7cad1 --- /dev/null +++ b/erp-frontend-vue/src/views/System/Dept/index.vue @@ -0,0 +1,334 @@ + + + + + diff --git a/erp-frontend-vue/src/views/System/Post/index.vue b/erp-frontend-vue/src/views/System/Post/index.vue new file mode 100644 index 0000000..b0ba8cb --- /dev/null +++ b/erp-frontend-vue/src/views/System/Post/index.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/erp-frontend-vue/src/views/System/Role/index.vue b/erp-frontend-vue/src/views/System/Role/index.vue new file mode 100644 index 0000000..e1827ee --- /dev/null +++ b/erp-frontend-vue/src/views/System/Role/index.vue @@ -0,0 +1,333 @@ + + + + + diff --git a/erp-frontend-vue/src/views/System/User/index.vue b/erp-frontend-vue/src/views/System/User/index.vue new file mode 100644 index 0000000..b2945a4 --- /dev/null +++ b/erp-frontend-vue/src/views/System/User/index.vue @@ -0,0 +1,531 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Warehouse/Issue/form.vue b/erp-frontend-vue/src/views/Warehouse/Issue/form.vue new file mode 100644 index 0000000..57628e6 --- /dev/null +++ b/erp-frontend-vue/src/views/Warehouse/Issue/form.vue @@ -0,0 +1,1408 @@ + + + + + diff --git a/erp-frontend-vue/src/views/Warehouse/Issue/index.vue b/erp-frontend-vue/src/views/Warehouse/Issue/index.vue new file mode 100644 index 0000000..b278f17 --- /dev/null +++ b/erp-frontend-vue/src/views/Warehouse/Issue/index.vue @@ -0,0 +1,459 @@ + + + + + diff --git a/erp-frontend-vue/tsconfig.app.json b/erp-frontend-vue/tsconfig.app.json new file mode 100644 index 0000000..1349c24 --- /dev/null +++ b/erp-frontend-vue/tsconfig.app.json @@ -0,0 +1,21 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "types": ["vite/client"], + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "exclude": ["src/**/__tests__/**", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/erp-frontend-vue/tsconfig.json b/erp-frontend-vue/tsconfig.json new file mode 100644 index 0000000..fec8c8e --- /dev/null +++ b/erp-frontend-vue/tsconfig.json @@ -0,0 +1,13 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/erp-frontend-vue/tsconfig.node.json b/erp-frontend-vue/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/erp-frontend-vue/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/erp-frontend-vue/vite.config.ts b/erp-frontend-vue/vite.config.ts new file mode 100644 index 0000000..009794b --- /dev/null +++ b/erp-frontend-vue/vite.config.ts @@ -0,0 +1,55 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import path from 'path' + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src') + } + }, + server: { + port: 5177, + strictPort: false, // 端口被占用时自动尝试下一个可用端口 + proxy: { + '/api': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/erp': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/mes': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/system': { + target: 'http://localhost:8080', + changeOrigin: true + }, + // 登录相关接口代理 + '/login': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/logout': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/captchaImage': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/getInfo': { + target: 'http://localhost:8080', + changeOrigin: true + }, + '/register': { + target: 'http://localhost:8080', + changeOrigin: true + } + } + } +}) diff --git a/erp-frontend-vue/vitest.config.ts b/erp-frontend-vue/vitest.config.ts new file mode 100644 index 0000000..03bd62a --- /dev/null +++ b/erp-frontend-vue/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' +import viteConfig from './vite.config' + +export default defineConfig({ + ...viteConfig, + test: { + environment: 'jsdom', + globals: true + } +})