diff --git a/app/electron.vite.config.ts b/app/electron.vite.config.ts index 2ac4f7c..594d92f 100644 --- a/app/electron.vite.config.ts +++ b/app/electron.vite.config.ts @@ -2,6 +2,7 @@ import { resolve } from 'path' import { readFileSync } from 'fs' import { defineConfig, externalizeDepsPlugin } from 'electron-vite' import vue from '@vitejs/plugin-vue' +import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite' const pkg = JSON.parse(readFileSync(resolve('package.json'), 'utf-8')) as { version: string } @@ -25,6 +26,14 @@ export default defineConfig({ define: { __APP_VERSION__: JSON.stringify(pkg.version) }, - plugins: [vue()] + plugins: [ + vue(), + // 构建时预编译多语言消息 + 使用 vue-i18n runtime 构建, + // 避免运行时 new Function() 编译消息(会被 CSP script-src 'self' 拦截导致白屏) + VueI18nPlugin({ + include: [resolve('src/renderer/src/i18n/locales/**')], + runtimeOnly: true + }) + ] } }) diff --git a/app/package-lock.json b/app/package-lock.json index 96ff68d..81fda64 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -14,6 +14,7 @@ "vue-i18n": "^9.14.4" }, "devDependencies": { + "@intlify/unplugin-vue-i18n": "^1.6.0", "@vitejs/plugin-vue": "^4.6.2", "electron": "20.3.12", "electron-builder": "^24.13.3", @@ -929,6 +930,36 @@ "node": ">=12" } }, + "node_modules/@intlify/bundle-utils": { + "version": "7.5.1", + "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-7.5.1.tgz", + "integrity": "sha512-UovJl10oBIlmYEcWw+VIHdKY5Uv5sdPG0b/b6bOYxGLln3UwB75+2dlc0F3Fsa0RhoznQ5Rp589/BZpABpE4Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "^9.4.0", + "@intlify/shared": "^9.4.0", + "acorn": "^8.8.2", + "escodegen": "^2.1.0", + "estree-walker": "^2.0.2", + "jsonc-eslint-parser": "^2.3.0", + "magic-string": "^0.30.0", + "mlly": "^1.2.0", + "source-map-js": "^1.0.1", + "yaml-eslint-parser": "^1.2.2" + }, + "engines": { + "node": ">= 14.16" + }, + "peerDependenciesMeta": { + "petite-vue-i18n": { + "optional": true + }, + "vue-i18n": { + "optional": true + } + } + }, "node_modules/@intlify/core-base": { "version": "9.14.4", "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz", @@ -973,6 +1004,46 @@ "url": "https://github.com/sponsors/kazupon" } }, + "node_modules/@intlify/unplugin-vue-i18n": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-1.6.0.tgz", + "integrity": "sha512-IGeFNWxdEvB12E/3Y/+nmIsGeTg5okPsK1XEtUUD/DdkHbVqUbJucMpHKeHF8Px55Qca551pQCs/g+VjNUt6KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/bundle-utils": "^7.4.0", + "@intlify/shared": "^9.4.0", + "@rollup/pluginutils": "^5.0.2", + "@vue/compiler-sfc": "^3.2.47", + "debug": "^4.3.3", + "fast-glob": "^3.2.12", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "pathe": "^1.0.0", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2", + "unplugin": "^1.1.0" + }, + "engines": { + "node": ">= 14.16" + }, + "peerDependencies": { + "petite-vue-i18n": "*", + "vue-i18n": "*", + "vue-i18n-bridge": "*" + }, + "peerDependenciesMeta": { + "petite-vue-i18n": { + "optional": true + }, + "vue-i18n": { + "optional": true + }, + "vue-i18n-bridge": { + "optional": true + } + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1186,6 +1257,44 @@ "node": ">= 10.0.0" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.6", "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz", @@ -1492,6 +1601,29 @@ "node": ">=14" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.14.0.tgz", @@ -1531,6 +1663,13 @@ "@types/ms": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -1757,6 +1896,29 @@ "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", @@ -2151,6 +2313,19 @@ "balanced-match": "^1.0.0" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.2", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", @@ -2590,6 +2765,13 @@ "node": ">=10" } }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, "node_modules/config-chain": { "version": "1.1.13", "resolved": "https://registry.npmmirror.com/config-chain/-/config-chain-1.1.13.tgz", @@ -3436,11 +3618,98 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", @@ -3476,6 +3745,23 @@ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -3497,6 +3783,16 @@ } ] }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -3527,6 +3823,19 @@ "node": ">=10" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", @@ -3741,6 +4050,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.14", "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.14.tgz", @@ -4115,7 +4437,6 @@ "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "optional": true, "engines": { "node": ">=0.10.0" } @@ -4134,7 +4455,6 @@ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "optional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -4142,6 +4462,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz", @@ -4272,6 +4602,38 @@ "node": ">=6" } }, + "node_modules/jsonc-eslint-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz", + "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, + "node_modules/jsonc-eslint-parser/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", @@ -4449,6 +4811,43 @@ "node": ">= 0.4" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", @@ -4575,6 +4974,26 @@ "node": ">=10" } }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", @@ -4793,6 +5212,13 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", @@ -4809,7 +5235,6 @@ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "optional": true, "engines": { "node": ">=12" }, @@ -4849,6 +5274,25 @@ } } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/pkg-up": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/pkg-up/-/pkg-up-3.1.0.tgz", @@ -4965,6 +5409,27 @@ "node": ">=6" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/read-config-file": { "version": "6.3.2", "resolved": "https://registry.npmmirror.com/read-config-file/-/read-config-file-6.3.2.tgz", @@ -5068,6 +5533,17 @@ "node": ">= 4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", @@ -5102,6 +5578,30 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5525,6 +6025,19 @@ "node": ">=6" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -5570,6 +6083,13 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", @@ -5579,6 +6099,20 @@ "node": ">= 4.0.0" } }, + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -5833,6 +6367,13 @@ "node": ">=10" } }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", @@ -5913,6 +6454,39 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-eslint-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz", + "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.0.0", + "yaml": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", @@ -6560,6 +7134,24 @@ "dev": true, "optional": true }, + "@intlify/bundle-utils": { + "version": "7.5.1", + "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-7.5.1.tgz", + "integrity": "sha512-UovJl10oBIlmYEcWw+VIHdKY5Uv5sdPG0b/b6bOYxGLln3UwB75+2dlc0F3Fsa0RhoznQ5Rp589/BZpABpE4Xw==", + "dev": true, + "requires": { + "@intlify/message-compiler": "^9.4.0", + "@intlify/shared": "^9.4.0", + "acorn": "^8.8.2", + "escodegen": "^2.1.0", + "estree-walker": "^2.0.2", + "jsonc-eslint-parser": "^2.3.0", + "magic-string": "^0.30.0", + "mlly": "^1.2.0", + "source-map-js": "^1.0.1", + "yaml-eslint-parser": "^1.2.2" + } + }, "@intlify/core-base": { "version": "9.14.4", "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz", @@ -6583,6 +7175,26 @@ "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.4.tgz", "integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==" }, + "@intlify/unplugin-vue-i18n": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-1.6.0.tgz", + "integrity": "sha512-IGeFNWxdEvB12E/3Y/+nmIsGeTg5okPsK1XEtUUD/DdkHbVqUbJucMpHKeHF8Px55Qca551pQCs/g+VjNUt6KA==", + "dev": true, + "requires": { + "@intlify/bundle-utils": "^7.4.0", + "@intlify/shared": "^9.4.0", + "@rollup/pluginutils": "^5.0.2", + "@vue/compiler-sfc": "^3.2.47", + "debug": "^4.3.3", + "fast-glob": "^3.2.12", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "pathe": "^1.0.0", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2", + "unplugin": "^1.1.0" + } + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -6740,6 +7352,32 @@ } } }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@parcel/watcher": { "version": "2.5.6", "resolved": "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz", @@ -6864,6 +7502,17 @@ "dev": true, "optional": true }, + "@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + } + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmmirror.com/@sindresorhus/is/-/is-0.14.0.tgz", @@ -6894,6 +7543,12 @@ "@types/ms": "*" } }, + "@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true + }, "@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -7100,6 +7755,19 @@ "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, + "acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", @@ -7408,6 +8076,15 @@ "balanced-match": "^1.0.0" } }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, "browserslist": { "version": "4.28.2", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz", @@ -7710,6 +8387,12 @@ } } }, + "confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true + }, "config-chain": { "version": "1.1.13", "resolved": "https://registry.npmmirror.com/config-chain/-/config-chain-1.1.13.tgz", @@ -8367,11 +9050,58 @@ "dev": true, "optional": true }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, "estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, "extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz", @@ -8396,6 +9126,19 @@ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -8407,6 +9150,15 @@ "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.2.tgz", "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==" }, + "fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -8436,6 +9188,15 @@ } } }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-up": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", @@ -8614,6 +9375,15 @@ } } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, "global-agent": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz", @@ -8880,8 +9650,7 @@ "version": "2.1.1", "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "optional": true + "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -8894,11 +9663,16 @@ "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "optional": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz", @@ -8994,6 +9768,26 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jsonc-eslint-parser": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz", + "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==", + "dev": true, + "requires": { + "acorn": "^8.5.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true + } + } + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", @@ -9157,6 +9951,30 @@ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "dependencies": { + "picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true + } + } + }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", @@ -9243,6 +10061,26 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "requires": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + }, + "dependencies": { + "pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + } + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", @@ -9402,6 +10240,12 @@ } } }, + "pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz", @@ -9417,8 +10261,7 @@ "version": "4.0.4", "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "optional": true + "dev": true }, "pify": { "version": "3.0.0", @@ -9437,6 +10280,25 @@ "vue-demi": "^0.14.10" } }, + "pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "requires": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + }, + "dependencies": { + "pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + } + } + }, "pkg-up": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/pkg-up/-/pkg-up-3.1.0.tgz", @@ -9518,6 +10380,12 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "read-config-file": { "version": "6.3.2", "resolved": "https://registry.npmmirror.com/read-config-file/-/read-config-file-6.3.2.tgz", @@ -9598,6 +10466,12 @@ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true }, + "reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true + }, "roarr": { "version": "2.15.4", "resolved": "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz", @@ -9622,6 +10496,15 @@ "fsevents": "~2.3.2" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9940,6 +10823,15 @@ "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", "dev": true }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmmirror.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -9969,12 +10861,28 @@ "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "devOptional": true }, + "ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, + "unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "requires": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + } + }, "update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -10107,6 +11015,12 @@ } } }, + "webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", @@ -10162,6 +11076,22 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true + }, + "yaml-eslint-parser": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz", + "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.0.0", + "yaml": "^2.0.0" + } + }, "yargs": { "version": "17.7.2", "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", diff --git a/app/package.json b/app/package.json index c934c0b..01a52dc 100644 --- a/app/package.json +++ b/app/package.json @@ -90,6 +90,7 @@ } }, "devDependencies": { + "@intlify/unplugin-vue-i18n": "^1.6.0", "@vitejs/plugin-vue": "^4.6.2", "electron": "20.3.12", "electron-builder": "^24.13.3", diff --git a/app/src/main/index.ts b/app/src/main/index.ts index ea2e963..e886cfb 100644 --- a/app/src/main/index.ts +++ b/app/src/main/index.ts @@ -106,6 +106,20 @@ function createWindow(): void { ) }) + // 页面加载失败(dev server 未就绪 / 生产文件缺失)时记录并提示,避免静默白屏 + mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDescription, validatedURL) => { + log.error('renderer did-fail-load', { errorCode, errorDescription, validatedURL }) + if (errorCode !== -3) { + mainWindow?.webContents.executeJavaScript( + `document.getElementById('app').innerHTML = '
[main:did-fail-load] ${errorCode} ${errorDescription}\\n${validatedURL}
'` + ).catch(() => undefined) + } + }) + + mainWindow.webContents.on('preload-error', (_event, preloadPath, error) => { + log.error('preload-error', { preloadPath, error: String(error) }) + }) + if (process.env.ELECTRON_RENDERER_URL) { mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL) } else { diff --git a/app/src/main/ipc/register-handlers.ts b/app/src/main/ipc/register-handlers.ts index 5a7c0a0..0711093 100644 --- a/app/src/main/ipc/register-handlers.ts +++ b/app/src/main/ipc/register-handlers.ts @@ -136,7 +136,12 @@ export function registerIpcHandlers(): void { assertReady() const dll = await loadDllModule() const code = dll.dllPrinterReset() - return code === CS_OK ? ok() : fail(code, '重置失败') + // 物理动作指令:负数为错误,0 或正数(状态/警告码)均视为指令已被打印机接受 + if (code < 0) { + log.warn('SAPI_PrinterResetprinter returned non-OK code', code) + return fail(code, '重置失败') + } + return ok({ code }) } catch (err) { return fail(CS_FAIL, String(err)) } @@ -148,7 +153,11 @@ export function registerIpcHandlers(): void { const dll = await loadDllModule() if (!dll.isRejectApiAvailable()) return fail(CS_FAIL, 'REJECT_API_UNAVAILABLE') const code = dll.dllPrinterReject() - return code === CS_OK ? ok() : fail(code, '废卡失败') + if (code < 0) { + log.warn('SAPI_PrinterMovetoreject returned non-OK code', code) + return fail(code, '废卡失败') + } + return ok({ code }) } catch (err) { return fail(CS_FAIL, String(err)) } @@ -161,7 +170,11 @@ export function registerIpcHandlers(): void { const dll = await loadDllModule() if (!dll.isUsbReaderApiAvailable()) return fail(CS_FAIL, 'USB_READER_API_UNAVAILABLE') const code = dll.dllPrinterMoveToUsbReader() - return code === CS_OK ? ok() : fail(code, '读卡失败') + if (code < 0) { + log.warn('SAPI_PrinterMovetousbreader returned non-OK code', code) + return fail(code, '读卡失败') + } + return ok({ code }) } catch (err) { return fail(CS_FAIL, String(err)) } @@ -174,7 +187,12 @@ export function registerIpcHandlers(): void { const dll = await loadDllModule() if (!dll.isHopperApiAvailable()) return fail(CS_FAIL, 'HOPPER_API_UNAVAILABLE') const code = dll.dllPrinterMoveToHopper() - return code === CS_OK ? ok() : fail(code, '退卡失败') + if (code < 0) { + log.warn('SAPI_PrinterMovetohopper returned non-OK code', code) + return fail(code, '退卡失败') + } + // 卡已物理退出但返回正数状态码时,不再误报失败 + return ok({ code }) } catch (err) { return fail(CS_FAIL, String(err)) } diff --git a/app/src/main/services/poll-manager.ts b/app/src/main/services/poll-manager.ts index 1131f5a..4223c6a 100644 --- a/app/src/main/services/poll-manager.ts +++ b/app/src/main/services/poll-manager.ts @@ -80,6 +80,15 @@ export function startJobPoll(id: string): void { const cancelled = r.jobState === 6 const finished = r.jobState === 100 const terminal = failed || cancelled + // 失败当刻立即取打印机错误串(与 USB 收集轮询对齐),避免渲染层事后取到通用/过期文案 + let errorMessage = '' + if (failed) { + try { + errorMessage = dll.dllGetPrinterErrorStr(-1) || '' + } catch (e) { + log.warn('GetPrinterErrorStr on job failed failed', e) + } + } const tick = { jobId, queryErrorCode: r.queryErrorCode, @@ -88,7 +97,8 @@ export function startJobPoll(id: string): void { terminal, failed, cancelled, - finished + finished, + errorMessage } emitTrace('[poll] job:poll-tick', tick) send('job:poll-tick', tick) diff --git a/app/src/main/services/work-dll.service.ts b/app/src/main/services/work-dll.service.ts index 878b8c7..7eec59e 100644 --- a/app/src/main/services/work-dll.service.ts +++ b/app/src/main/services/work-dll.service.ts @@ -44,6 +44,8 @@ let SAPI_PrinterMovetoreject: any = null // eslint-disable-next-line @typescript-eslint/no-explicit-any let SAPI_PrinterMovetousbreader: any = null // eslint-disable-next-line @typescript-eslint/no-explicit-any +let SAPI_PrinterMovetohopper: any = null +// eslint-disable-next-line @typescript-eslint/no-explicit-any let SAPI_GetPrinterCardPosition: any = null // eslint-disable-next-line @typescript-eslint/no-explicit-any let SAPI_PrinterCheckstatus: any = null @@ -55,6 +57,7 @@ let hasCheckstatusApi = false let hasCancelApi = false let hasUploadApi = false let hasUsbReaderApi = false +let hasHopperApi = false let loggedCancelMissing = false let loggedRejectMissing = false @@ -206,6 +209,7 @@ function loadLibrary(): void { cancel: hasCancelApi, reject: hasRejectApi, usbReader: hasUsbReaderApi, + hopper: hasHopperApi, cardPosition: hasCardPositionApi, checkstatus: hasCheckstatusApi }) @@ -231,6 +235,11 @@ export function isUsbReaderApiAvailable(): boolean { return hasUsbReaderApi } +export function isHopperApiAvailable(): boolean { + loadLibrary() + return hasHopperApi +} + export function isCardPositionApiAvailable(): boolean { loadLibrary() return hasCardPositionApi diff --git a/app/src/preload/index.ts b/app/src/preload/index.ts index c40aaae..ba68e61 100644 --- a/app/src/preload/index.ts +++ b/app/src/preload/index.ts @@ -7,6 +7,8 @@ const channels = { 'dll:printer-status', 'dll:printer-reset', 'dll:printer-reject', + 'dll:printer-read-card', + 'dll:printer-eject-card', 'dll:printer-error-str', 'dll:job-create', 'dll:job-cancel', diff --git a/app/src/renderer/src/assets/icons/index.ts b/app/src/renderer/src/assets/icons/index.ts index 5a3baeb..b8795f3 100644 --- a/app/src/renderer/src/assets/icons/index.ts +++ b/app/src/renderer/src/assets/icons/index.ts @@ -15,7 +15,9 @@ export const ICON_NAMES = [ 'stop', 'warning', 'id-card', - 'eject' + 'eject', + 'eye', + 'eye-slash' ] as const export type IconName = (typeof ICON_NAMES)[number] @@ -38,5 +40,7 @@ export const ICON_FA_CLASS: Record = { stop: 'fas fa-stop', warning: 'fas fa-exclamation-triangle', 'id-card': 'fas fa-id-card', - eject: 'fas fa-eject' + eject: 'fas fa-eject', + eye: 'fas fa-eye', + 'eye-slash': 'fas fa-eye-slash' } diff --git a/app/src/renderer/src/composables/usePrinterStatus.ts b/app/src/renderer/src/composables/usePrinterStatus.ts index 030ecda..b2fe2d9 100644 --- a/app/src/renderer/src/composables/usePrinterStatus.ts +++ b/app/src/renderer/src/composables/usePrinterStatus.ts @@ -1,12 +1,28 @@ import { dllPrinterInfo, dllPrinterStatus, parsePrinterInfo } from '@/api/cardsoon' import { useConfigStore } from '@/stores/config' +/** 状态是否表示打印机未连接/不可用 */ +function isDisconnectedStatus(text: string): boolean { + return !text || text === '—' || text.includes('未连接') || text.includes('Not connected') +} + +let prevStatusText = '' + export async function refreshLiveStatus(): Promise { const store = useConfigStore() try { const r = await dllPrinterStatus() if (r.ok && r.data?.statusText) { - store.setPrinter({ ...store.printer, statusText: r.data.statusText }) + const newStatus = r.data.statusText + // 检测状态变化:从未连接 → 已连接时,立即刷新打印机信息(型号/isSingleSide等) + const wasDisconnected = isDisconnectedStatus(prevStatusText) + const nowConnected = !isDisconnectedStatus(newStatus) + store.setPrinter({ ...store.printer, statusText: newStatus }) + if (wasDisconnected && nowConnected) { + // 打印机刚连上,立即拉取型号/色带等信息 + void refreshPrinterInfo() + } + prevStatusText = newStatus } } catch { /* ignore */ diff --git a/app/src/renderer/src/i18n/locales/en-US.ts b/app/src/renderer/src/i18n/locales/en-US.ts index 2694b89..31f2568 100644 --- a/app/src/renderer/src/i18n/locales/en-US.ts +++ b/app/src/renderer/src/i18n/locales/en-US.ts @@ -36,8 +36,10 @@ export default { home: { toolsTitle: 'Tools', tasksTitle: 'Tasks', + readCard: 'Read', + ejectCard: 'Eject', dataDistribute: 'Data Distribution', - dataDistributeDesc: 'Distribute data to print cards', + dataDistributeDesc: 'Distribute data onto cards', dataCollect: 'Data Collection', dataCollectDesc: 'Collect data from cards' }, @@ -53,8 +55,12 @@ export default { dongleHint: '(101 = unlimited)', dongleAuthLabel: 'Auth Code', donglePassword: 'Enter authorization code', + showAuth: 'Show auth code', + hideAuth: 'Hide auth code', templatePreview: 'Label Preview', addLabel: 'Add Label', + removeLabel: 'Remove', + templateRemoved: 'Label removed', doubleSide: 'Double-sided', singleSideHint: 'Single-side printer detected, please select a side (front or back)', frontSide: 'Front', @@ -172,6 +178,9 @@ export default { templateNotExist: 'Template file does not exist', printFlagMismatch: 'Print side mismatch, please reselect', singleSideMustPick: 'Single-side printer, please select front or back', + pickSideRequired: 'Please select a print side (front or back) before submitting', + printerNotConnected: 'Printer not connected, please connect the printer first', + printerNotReady: 'Printer not connected or in fault (e.g. card jam). Please fix the device before submitting', dongleAuthRequired: 'Please enter authorization code', dongleCountInvalid: 'Dongle count must be 0 or 1-101', createJobFailed: 'Failed to create task', diff --git a/app/src/renderer/src/i18n/locales/zh-CN.ts b/app/src/renderer/src/i18n/locales/zh-CN.ts index 54da060..52a9892 100644 --- a/app/src/renderer/src/i18n/locales/zh-CN.ts +++ b/app/src/renderer/src/i18n/locales/zh-CN.ts @@ -36,6 +36,8 @@ export default { home: { toolsTitle: '工具', tasksTitle: '任务', + readCard: '读卡', + ejectCard: '退卡', dataDistribute: '数据分发', dataDistributeDesc: '分发数据到打印卡片', dataCollect: '数据收集', @@ -53,12 +55,16 @@ export default { dongleHint: '(101 为不限次数)', dongleAuthLabel: '授权码', donglePassword: '请输入授权码', + showAuth: '显示授权码', + hideAuth: '隐藏授权码', templatePreview: '标签预览', addLabel: '添加标签', + removeLabel: '移除标签', + templateRemoved: '已移除标签', doubleSide: '双面', singleSideHint: '当前为单面打印机,请选择打印面(正面或背面)', - frontSide: 'FRONT', - backSide: 'BACK', + frontSide: '正面', + backSide: '背面', imageFieldNotSelected: '未选择图片', templateLoaded: '已加载标签模板', templateNoPreview: '模板已打开,但未解析到可预览内容', @@ -160,7 +166,9 @@ export default { startUsbFailed: '启动 USB 收集失败', usbWaitComplete: '请先等待 USB 收集完成', cannotEnterRunning: '无法进入运行页,已取消任务', - initFailedHint: '初始化失败,请检查任务目录权限' + initFailedHint: '初始化失败,请检查任务目录权限', + waitTimeoutCollect: '等待时间较长:请检查 U 盘数据线是否连接、卡片是否插好(可把卡插反后重试)', + waitTimeoutDistribute: '等待进卡时间较长:请检查卡片是否到位、打印机是否夹卡或报错' }, validation: { noTask: '请配置拷贝路径或打印模板', @@ -173,6 +181,8 @@ export default { printFlagMismatch: '打印面数不匹配,请重新选择', singleSideMustPick: '当前为单面打印机,请选择正面或背面', pickSideRequired: '请选择打印面(正面或背面)后再提交', + printerNotConnected: '打印机未连接,请先连接打印机后再提交', + printerNotReady: '打印机未连接或设备异常(如夹卡),请排除故障后再提交', dongleAuthRequired: '请输入授权码', dongleCountInvalid: '加密狗次数须为 0 或 1-101', createJobFailed: '创建任务失败', diff --git a/app/src/renderer/src/i18n/locales/zh-TW.ts b/app/src/renderer/src/i18n/locales/zh-TW.ts index 9b156aa..2f62244 100644 --- a/app/src/renderer/src/i18n/locales/zh-TW.ts +++ b/app/src/renderer/src/i18n/locales/zh-TW.ts @@ -36,6 +36,8 @@ export default { home: { toolsTitle: '工具', tasksTitle: '任務', + readCard: '讀卡', + ejectCard: '退卡', dataDistribute: '數據分發', dataDistributeDesc: '分發數據到列印卡片', dataCollect: '數據收集', @@ -53,12 +55,16 @@ export default { dongleHint: '(101 為不限次數)', dongleAuthLabel: '授權碼', donglePassword: '請輸入授權碼', + showAuth: '顯示授權碼', + hideAuth: '隱藏授權碼', templatePreview: '標籤預覽', addLabel: '新增標籤', + removeLabel: '移除標籤', + templateRemoved: '已移除標籤', doubleSide: '雙面', singleSideHint: '當前為單面印表機,請選擇列印面(正面或背面)', - frontSide: 'FRONT', - backSide: 'BACK', + frontSide: '正面', + backSide: '背面', imageFieldNotSelected: '未選擇圖片', templateLoaded: '已載入標籤模板', templateNoPreview: '模板已開啟,但未解析到可預覽內容', @@ -173,6 +179,8 @@ export default { printFlagMismatch: '列印面數不匹配,請重新選擇', singleSideMustPick: '當前為單面印表機,請選擇正面或背面', pickSideRequired: '請選擇列印面(正面或背面)後再提交', + printerNotConnected: '印表機未連接,請先連接印表機後再提交', + printerNotReady: '印表機未連接或設備異常(如夾卡),請排除故障後再提交', dongleAuthRequired: '請輸入授權碼', dongleCountInvalid: '加密狗次數須為 0 或 1-101', createJobFailed: '建立任務失敗', diff --git a/app/src/renderer/src/main.ts b/app/src/renderer/src/main.ts index bd8da41..20a5a8d 100644 --- a/app/src/renderer/src/main.ts +++ b/app/src/renderer/src/main.ts @@ -24,73 +24,92 @@ import './styles/shell.css' -window.cardsoonApi.on('app:trace', (payload) => { - - const p = payload as { level: string; message: string; data?: Record } - - if (p.level === 'error') console.error(p.message, p.data ?? '') - - else console.log(p.message, p.data ?? '') - -}) - +// preload 未注入时(如普通浏览器调试/ preload 加载失败)不再白屏,直接跳过桌面侧能力 +const hasApi = typeof window.cardsoonApi !== 'undefined' +if (hasApi) { + window.cardsoonApi.on('app:trace', (payload) => { + const p = payload as { level: string; message: string; data?: Record } + if (p.level === 'error') console.error(p.message, p.data ?? '') + else console.log(p.message, p.data ?? '') + }) +} async function setTrace(on: boolean): Promise { - await window.cardsoonApi.invoke('config:set', { traceEnabled: on }) - console.info(`[trace] 控制台日志已${on ? '开启' : '关闭'}`) +} +const w = window as Window & { trace?: (on?: boolean) => Promise; dllTrace?: (on?: boolean) => Promise } + +if (hasApi) { + w.trace = async (on = true) => setTrace(on) + w.dllTrace = w.trace + + void (async () => { + const cfg = await configGet() + const on = cfg.ok && cfg.data?.traceEnabled === true + console.info(`[trace] 控制台日志: ${on ? '已开启' : '已关闭'},执行 trace(false) 关闭`) + })() } -const w = window as Window & { trace?: (on?: boolean) => Promise; dllTrace?: (on?: boolean) => Promise } +/** + * 渲染进程启动崩溃兜底:把白屏变成可见错误面板, + * 便于在无法打开 DevTools 的现场截图定位问题。 + * 仅在 Vue 挂载前/挂载瞬间触发的错误会覆盖界面; + * 挂载完成后的运行时错误只进控制台,不破坏已有界面。 + */ +let appMounted = false -w.trace = async (on = true) => setTrace(on) +function showFatalError(stage: string, err: unknown): void { + if (appMounted) { + console.error(`[renderer:${stage}]`, err) + return + } + const el = document.getElementById('app') + if (!el) return + const detail = err instanceof Error ? `${err.message}\n\n${err.stack ?? ''}` : String(err) + el.innerHTML = `
[renderer:${stage}]\n${detail}
` +} -w.dllTrace = w.trace +window.addEventListener('error', (e) => { + showFatalError('window.onerror', e.error ?? e.message) +}) +window.addEventListener('unhandledrejection', (e) => { + showFatalError('unhandledrejection', e.reason) +}) +try { + const app = createApp(App) + app.config.errorHandler = (err, _instance, info) => { + console.error('[vue errorHandler]', err, info) + if (!appMounted) showFatalError(`vue:${info}`, err) + } -void (async () => { + const pinia = createPinia() - const cfg = await configGet() + app.use(pinia) - const on = cfg.ok && cfg.data?.traceEnabled === true + app.use(i18n) - console.info(`[trace] 控制台日志: ${on ? '已开启' : '已关闭'},执行 trace(false) 关闭`) + app.use(router) -})() + void (async () => { + await useNetworkAuthStore().loadFromStorage() + const dongleStore = useDongleAuthStore() + await dongleStore.loadFromSecrets() -const app = createApp(App) + useDistributeFormStore().dongleAuthCode = dongleStore.authCode + })() -const pinia = createPinia() - -app.use(pinia) - -app.use(i18n) - -app.use(router) - - - -void (async () => { - - await useNetworkAuthStore().loadFromStorage() - - const dongleStore = useDongleAuthStore() - - await dongleStore.loadFromSecrets() - - useDistributeFormStore().dongleAuthCode = dongleStore.authCode - -})() - - - -app.mount('#app') + app.mount('#app') + appMounted = true +} catch (err) { + showFatalError('bootstrap', err) +} diff --git a/app/src/renderer/src/stores/job.ts b/app/src/renderer/src/stores/job.ts index 0f02440..3731c1a 100644 --- a/app/src/renderer/src/stores/job.ts +++ b/app/src/renderer/src/stores/job.ts @@ -18,6 +18,11 @@ export const useJobStore = defineStore('job', { clearActiveJob() { this.jobId = '' }, + /** 重置次数计数器(不影响当前 jobId/lastJobJson) */ + resetCounts() { + this.successCount = 0 + this.failCount = 0 + }, reset() { this.jobId = '' this.lastJobJson = '' diff --git a/app/src/renderer/src/styles/icons-font.css b/app/src/renderer/src/styles/icons-font.css index 5c7b165..596c8b8 100644 --- a/app/src/renderer/src/styles/icons-font.css +++ b/app/src/renderer/src/styles/icons-font.css @@ -65,3 +65,15 @@ .fa-exclamation-triangle::before { content: '\f071'; } +.fa-id-card::before { + content: '\f2c2'; +} +.fa-eject::before { + content: '\f052'; +} +.fa-eye::before { + content: '\f06e'; +} +.fa-eye-slash::before { + content: '\f070'; +} diff --git a/app/src/renderer/src/styles/pages/page2.css b/app/src/renderer/src/styles/pages/page2.css index 178b33c..5100cbe 100644 --- a/app/src/renderer/src/styles/pages/page2.css +++ b/app/src/renderer/src/styles/pages/page2.css @@ -73,22 +73,99 @@ color: var(--cs-primary); } -/* 读卡 / 退卡 并排按钮行 */ -.m-tool-row { +/* 不支持的工具按钮(如当前 DLL 无废卡接口):置灰禁用 */ +.m-tool-btn:disabled { + background: #f1f3f5; + border-color: #e9ecef; + color: #adb5bd; + cursor: not-allowed; + box-shadow: none; +} + +.m-tool-btn:disabled:hover { + border-color: #e9ecef; + box-shadow: none; +} + +.m-tool-btn:disabled i, +.m-tool-btn:disabled:hover i { + color: #ced4da; +} + +/* ========== 读卡 / 退卡 PK 式斜切分隔按钮 ========== */ +.m-tool-versus { + position: relative; + width: 100%; + height: 48px; + background: #fff; + border: 1px solid #dee2e6; + border-radius: 6px; + overflow: hidden; display: flex; - gap: 8px; + transition: border-color 0.2s ease, box-shadow 0.2s ease; } -.m-tool-btn--half { - flex: 1; - min-width: 0; - justify-content: center; - padding: 0 8px; +.m-tool-versus:hover { + border-color: var(--cs-primary); + box-shadow: 0 4px 12px rgba(0, 128, 0, 0.10); +} + +.m-versus-half { + position: relative; + z-index: 1; + width: 50%; + height: 100%; + border: none; + background: transparent; + display: flex; + align-items: center; gap: 6px; + font-size: 12px; + font-weight: 700; + color: #495057; + cursor: pointer; + white-space: nowrap; + transition: background 0.18s ease, color 0.18s ease; } -.m-tool-btn--half i { - width: 16px; +.m-versus-half span { + white-space: nowrap; + overflow: hidden; + text-overflow: clip; +} + +.m-versus-half i { + font-size: 14px; + flex-shrink: 0; + color: #6c757d; + transition: color 0.18s ease; +} + +/* 左半:内容朝中间(右对齐),右边缘斜切为 \ 形 */ +.m-versus-half--read { + justify-content: flex-end; + padding-right: 12px; + clip-path: polygon(0 0, calc(100% - 9px) 0, calc(100% + 9px) 100%, 0 100%); +} + +/* 右半:内容朝中间(左对齐),左边缘斜切为 \ 形 */ +.m-versus-half--eject { + justify-content: flex-start; + padding-left: 12px; + clip-path: polygon(calc(0% - 9px) 0, 100% 0, 100% 100%, calc(0% + 9px) 100%); +} + +.m-versus-half:hover { + background: rgba(0, 128, 0, 0.08); + color: var(--cs-primary); +} + +.m-versus-half:hover i { + color: var(--cs-primary); +} + +.m-versus-half:active { + background: rgba(0, 128, 0, 0.16); } /* ========== 垂直分隔线 ========== */ diff --git a/app/src/renderer/src/styles/pages/page4.css b/app/src/renderer/src/styles/pages/page4.css index df68f15..e038dad 100644 --- a/app/src/renderer/src/styles/pages/page4.css +++ b/app/src/renderer/src/styles/pages/page4.css @@ -111,7 +111,7 @@ } .m-panel-toolbar .c-checkbox-item .c-input.dog-auth { - width: 110px; + width: 140px; height: 20px; padding: 0 4px; font-size: 9px; @@ -120,6 +120,36 @@ margin-left: 3px; } +/* 授权码输入框容器 + 小眼睛 */ +.m-panel-toolbar .c-checkbox-item .dog-auth-wrap { + position: relative; + display: inline-flex; + align-items: center; + margin-left: 3px; +} + +.m-panel-toolbar .c-checkbox-item .dog-auth-toggle { + position: absolute; + right: 2px; + top: 50%; + transform: translateY(-50%); + background: none; + border: none; + cursor: pointer; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + color: #adb5bd; + font-size: 10px; +} + +.m-panel-toolbar .c-checkbox-item .dog-auth-toggle:hover { + color: #495057; +} + /* 授权码标签 */ .m-panel-toolbar .c-checkbox-item .dog-auth-label { font-size: 9px; diff --git a/app/src/renderer/src/types/ipc.ts b/app/src/renderer/src/types/ipc.ts index 9b931ff..c77b778 100644 --- a/app/src/renderer/src/types/ipc.ts +++ b/app/src/renderer/src/types/ipc.ts @@ -25,6 +25,8 @@ export interface JobPollPayload { failed: boolean cancelled: boolean finished: boolean + /** 任务失败当刻由主进程取到的打印机错误串 */ + errorMessage?: string } export interface UsbPollPayload { diff --git a/app/src/renderer/src/utils/validateJobPreflight.ts b/app/src/renderer/src/utils/validateJobPreflight.ts index 6446037..07f3fdb 100644 --- a/app/src/renderer/src/utils/validateJobPreflight.ts +++ b/app/src/renderer/src/utils/validateJobPreflight.ts @@ -1,6 +1,6 @@ import type { DistributeFormState } from '@/stores/distributeForm' import i18n from '@/i18n' -import { fsPathExists } from '@/api/cardsoon' +import { fsPathExists, dllPrinterStatus } from '@/api/cardsoon' import { resolveJobTasks, validateJobConfig } from '@/utils/validateJobConfig' import { resolveCopyNetworkHosts } from '@/utils/copyNetworkHosts' import { useNetworkAuthStore } from '@/stores/networkAuth' @@ -22,11 +22,47 @@ export function printFlagMismatch(printFlag: number, templateFlag: number): bool ) } +/** + * 提交前打印机硬件状态预检(分发/收集共用)。 + * - 返回非 null 字符串:拦截提交并提示(未连接 / 设备故障码,如夹卡) + * - 返回 null:放行(含 DLL 不支持状态查询、查询异常等无法判断的情况,交给后续流程) + * 注:夹卡等具体故障码表待 DLL 侧提供后,可在此按 statusCode 精确映射文案。 + */ +export async function preflightPrinterStatus(): Promise { + try { + const r = await dllPrinterStatus() + if (!r.ok) { + const msg = r.message || '' + // DLL 不支持状态查询时无法判断,放行交给后续流程 + if (msg.includes('不支持') || msg.toLowerCase().includes('not supported')) { + return null + } + return t('validation.printerNotReady') + } + const data = r.data + if (!data) return t('validation.printerNotReady') + // 健康:主进程返回了明确的非负状态码(0 空闲 / 忙碌 / 打印中) + if (typeof data.statusCode === 'number' && data.statusCode >= 0) return null + // 缓存兜底 + liveError:实时查询失败(断连 / 故障码) + if (data.fromCache && data.liveError) return t('validation.printerNotReady') + // 无状态码(未初始化 / 未知异常):保守拦截 + return t('validation.printerNotReady') + } catch { + // 查询异常时不阻塞,让后续流程处理 + return null + } +} + export async function validateJobPreflight(form: DistributeFormState): Promise { const err = validateJobConfig(form) if (err) return err + // 打印机硬件预检:打印/拷贝任务都需要设备,未连接或故障码(如夹卡)时拦截提交 const { hasCopy, hasPrint } = resolveJobTasks(form) + if (hasPrint || hasCopy) { + const fault = await preflightPrinterStatus() + if (fault) return fault + } if (hasCopy) { const paths = form.pathList.map((x) => x.path) diff --git a/app/src/renderer/src/views/DataCollectView.vue b/app/src/renderer/src/views/DataCollectView.vue index 63dab3f..13482f3 100644 --- a/app/src/renderer/src/views/DataCollectView.vue +++ b/app/src/renderer/src/views/DataCollectView.vue @@ -67,6 +67,7 @@ import AppIcon from '@/components/AppIcon.vue' import { useCollectStore } from '@/stores/collect' import { useAppStore } from '@/stores/app' import { dialogOpenDirectory, dllUsbCopy } from '@/api/cardsoon' +import { preflightPrinterStatus } from '@/utils/validateJobPreflight' const router = useRouter() const { t } = useI18n() @@ -102,6 +103,12 @@ async function onSubmit(): Promise { notify.warning(t('notify.selectDirFirst')) return } + // 打印机硬件预检:未连接或故障码(如夹卡)时拦截提交 + const fault = await preflightPrinterStatus() + if (fault) { + notify.warning(fault) + return + } const r = await dllUsbCopy(dest, collectStore.cardOutput) if (!r.ok) { notify.error(r.message || t('notify.startUsbFailed')) diff --git a/app/src/renderer/src/views/DistributeConfigView.vue b/app/src/renderer/src/views/DistributeConfigView.vue index 0fd3d42..74faf00 100644 --- a/app/src/renderer/src/views/DistributeConfigView.vue +++ b/app/src/renderer/src/views/DistributeConfigView.vue @@ -70,13 +70,23 @@ {{ dongleHint }} @@ -106,6 +116,14 @@ +
@@ -202,7 +220,7 @@