优化bug
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
|
import { readFileSync } from 'fs'
|
||||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
const pkg = JSON.parse(readFileSync(resolve('package.json'), 'utf-8')) as { version: string }
|
||||||
|
|
||||||
const sharedAlias = { '@shared': resolve('src/shared') }
|
const sharedAlias = { '@shared': resolve('src/shared') }
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -19,6 +22,9 @@ export default defineConfig({
|
|||||||
...sharedAlias
|
...sharedAlias
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
define: {
|
||||||
|
__APP_VERSION__: JSON.stringify(pkg.version)
|
||||||
|
},
|
||||||
plugins: [vue()]
|
plugins: [vue()]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Generated
+118
-68
@@ -10,7 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-log": "^5.1.2",
|
"electron-log": "^5.1.2",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"koffi": "^2.9.0"
|
"koffi": "^2.9.0",
|
||||||
|
"vue-i18n": "^9.14.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^4.6.2",
|
"@vitejs/plugin-vue": "^4.6.2",
|
||||||
@@ -166,7 +167,6 @@
|
|||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,6 @@
|
|||||||
"version": "7.28.5",
|
"version": "7.28.5",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
@@ -206,7 +205,6 @@
|
|||||||
"version": "7.29.3",
|
"version": "7.29.3",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz",
|
||||||
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.29.0"
|
"@babel/types": "^7.29.0"
|
||||||
},
|
},
|
||||||
@@ -268,7 +266,6 @@
|
|||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
|
||||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.27.1",
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
"@babel/helper-validator-identifier": "^7.28.5"
|
"@babel/helper-validator-identifier": "^7.28.5"
|
||||||
@@ -932,6 +929,50 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@intlify/core-base": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@intlify/message-compiler": "9.14.4",
|
||||||
|
"@intlify/shared": "9.14.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@intlify/message-compiler": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@intlify/shared": "9.14.4",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@intlify/shared": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@isaacs/cliui": {
|
"node_modules/@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
@@ -1060,8 +1101,7 @@
|
|||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@jridgewell/trace-mapping": {
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.31",
|
"version": "0.3.31",
|
||||||
@@ -1585,7 +1625,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.34.tgz",
|
||||||
"integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==",
|
"integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.3",
|
"@babel/parser": "^7.29.3",
|
||||||
"@vue/shared": "3.5.34",
|
"@vue/shared": "3.5.34",
|
||||||
@@ -1598,7 +1637,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz",
|
||||||
"integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==",
|
"integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-core": "3.5.34",
|
"@vue/compiler-core": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -1608,7 +1646,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz",
|
||||||
"integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==",
|
"integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.29.3",
|
"@babel/parser": "^7.29.3",
|
||||||
"@vue/compiler-core": "3.5.34",
|
"@vue/compiler-core": "3.5.34",
|
||||||
@@ -1625,7 +1662,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz",
|
||||||
"integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==",
|
"integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.34",
|
"@vue/compiler-dom": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -1634,8 +1670,7 @@
|
|||||||
"node_modules/@vue/devtools-api": {
|
"node_modules/@vue/devtools-api": {
|
||||||
"version": "6.6.4",
|
"version": "6.6.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@vue/language-core": {
|
"node_modules/@vue/language-core": {
|
||||||
"version": "1.8.27",
|
"version": "1.8.27",
|
||||||
@@ -1666,7 +1701,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.34.tgz",
|
||||||
"integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==",
|
"integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
}
|
}
|
||||||
@@ -1675,7 +1709,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.34.tgz",
|
||||||
"integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==",
|
"integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.34",
|
"@vue/reactivity": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -1685,7 +1718,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz",
|
||||||
"integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==",
|
"integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/reactivity": "3.5.34",
|
"@vue/reactivity": "3.5.34",
|
||||||
"@vue/runtime-core": "3.5.34",
|
"@vue/runtime-core": "3.5.34",
|
||||||
@@ -1697,7 +1729,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.34.tgz",
|
||||||
"integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==",
|
"integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-ssr": "3.5.34",
|
"@vue/compiler-ssr": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -1709,8 +1740,7 @@
|
|||||||
"node_modules/@vue/shared": {
|
"node_modules/@vue/shared": {
|
||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.34.tgz",
|
||||||
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==",
|
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.9.10",
|
"version": "0.9.10",
|
||||||
@@ -2676,8 +2706,7 @@
|
|||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/de-indent": {
|
"node_modules/de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -3275,7 +3304,6 @@
|
|||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
||||||
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
},
|
},
|
||||||
@@ -3411,8 +3439,7 @@
|
|||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/extract-zip": {
|
"node_modules/extract-zip": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -4396,7 +4423,6 @@
|
|||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
}
|
}
|
||||||
@@ -4565,7 +4591,6 @@
|
|||||||
"version": "3.3.12",
|
"version": "3.3.12",
|
||||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz",
|
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -4777,8 +4802,7 @@
|
|||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/picomatch": {
|
"node_modules/picomatch": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
@@ -4854,7 +4878,6 @@
|
|||||||
"version": "8.5.15",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz",
|
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz",
|
||||||
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -5272,7 +5295,6 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -5539,7 +5561,7 @@
|
|||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz",
|
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz",
|
||||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -5695,7 +5717,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.34.tgz",
|
||||||
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.34",
|
"@vue/compiler-dom": "3.5.34",
|
||||||
"@vue/compiler-sfc": "3.5.34",
|
"@vue/compiler-sfc": "3.5.34",
|
||||||
@@ -5738,6 +5759,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-i18n": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@intlify/core-base": "9.14.4",
|
||||||
|
"@intlify/shared": "9.14.4",
|
||||||
|
"@vue/devtools-api": "^6.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/kazupon"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "4.6.4",
|
"version": "4.6.4",
|
||||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
|
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
|
||||||
@@ -6050,14 +6091,12 @@
|
|||||||
"@babel/helper-string-parser": {
|
"@babel/helper-string-parser": {
|
||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@babel/helper-validator-identifier": {
|
"@babel/helper-validator-identifier": {
|
||||||
"version": "7.28.5",
|
"version": "7.28.5",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@babel/helper-validator-option": {
|
"@babel/helper-validator-option": {
|
||||||
"version": "7.27.1",
|
"version": "7.27.1",
|
||||||
@@ -6079,7 +6118,6 @@
|
|||||||
"version": "7.29.3",
|
"version": "7.29.3",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.3.tgz",
|
||||||
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
"integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/types": "^7.29.0"
|
"@babel/types": "^7.29.0"
|
||||||
}
|
}
|
||||||
@@ -6123,7 +6161,6 @@
|
|||||||
"version": "7.29.0",
|
"version": "7.29.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz",
|
||||||
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/helper-string-parser": "^7.27.1",
|
"@babel/helper-string-parser": "^7.27.1",
|
||||||
"@babel/helper-validator-identifier": "^7.28.5"
|
"@babel/helper-validator-identifier": "^7.28.5"
|
||||||
@@ -6523,6 +6560,29 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@intlify/core-base": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-vtZCt7NqWhKEtHa3SD/322DlgP5uR9MqWxnE0y8Q0tjDs9H5Lxhss+b5wv8rmuXRoHKLESNgw9d+EN9ybBbj9g==",
|
||||||
|
"requires": {
|
||||||
|
"@intlify/message-compiler": "9.14.4",
|
||||||
|
"@intlify/shared": "9.14.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@intlify/message-compiler": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-vcyCLiVRN628U38c3PbahrhbbXrckrM9zpy0KZVlDk2Z0OnGwv8uQNNXP3twwGtfLsCf4gu3ci6FMIZnPaqZsw==",
|
||||||
|
"requires": {
|
||||||
|
"@intlify/shared": "9.14.4",
|
||||||
|
"source-map-js": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@intlify/shared": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-P9zv6i1WvMc9qDBWvIgKkymjY2ptIiQ065PjDv7z7fDqH3J/HBRBN5IoiR46r/ujRcU7hCuSIZWvCAFCyuOYZA=="
|
||||||
|
},
|
||||||
"@isaacs/cliui": {
|
"@isaacs/cliui": {
|
||||||
"version": "8.0.2",
|
"version": "8.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
@@ -6617,8 +6677,7 @@
|
|||||||
"@jridgewell/sourcemap-codec": {
|
"@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.5",
|
"version": "1.5.5",
|
||||||
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@jridgewell/trace-mapping": {
|
"@jridgewell/trace-mapping": {
|
||||||
"version": "0.3.31",
|
"version": "0.3.31",
|
||||||
@@ -6923,7 +6982,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.34.tgz",
|
||||||
"integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==",
|
"integrity": "sha512-s9cLyK5mLcvZ4Agva5QgRsQyLKvts9WbU9DB6NqiZkkGEdwmcEiylj5Jbwkp680drF/NNCV8OlAJSe+yMLxaJw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/parser": "^7.29.3",
|
"@babel/parser": "^7.29.3",
|
||||||
"@vue/shared": "3.5.34",
|
"@vue/shared": "3.5.34",
|
||||||
@@ -6936,7 +6994,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.34.tgz",
|
||||||
"integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==",
|
"integrity": "sha512-EbF/T++k0e2MMZlJsBhzK8Sgwt0HcIPOhzn1CTB/lv6sQcyk+OWf8YeiLxZp3ro7MbbLcAfAJ6sEvjFWuNgUCw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-core": "3.5.34",
|
"@vue/compiler-core": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -6946,7 +7003,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.34.tgz",
|
||||||
"integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==",
|
"integrity": "sha512-D/ihr6uZeIt6r+pVZf46RWT1fAsLFMbUP7k8G1VkiiWexriED9GrX3echHd4Abbt17zjlfiFJ8z7a3BxZOPNjg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/parser": "^7.29.3",
|
"@babel/parser": "^7.29.3",
|
||||||
"@vue/compiler-core": "3.5.34",
|
"@vue/compiler-core": "3.5.34",
|
||||||
@@ -6963,7 +7019,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.34.tgz",
|
||||||
"integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==",
|
"integrity": "sha512-cDtTHKibkThKGHH1SP+WdccquNRYQDFH6rRjQCqT9G2ltFAfoR5pUftpab/z+aM5mW9HLLVQW7hfKKQe/1GBeQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-dom": "3.5.34",
|
"@vue/compiler-dom": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -6972,8 +7027,7 @@
|
|||||||
"@vue/devtools-api": {
|
"@vue/devtools-api": {
|
||||||
"version": "6.6.4",
|
"version": "6.6.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@vue/language-core": {
|
"@vue/language-core": {
|
||||||
"version": "1.8.27",
|
"version": "1.8.27",
|
||||||
@@ -6996,7 +7050,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.34.tgz",
|
||||||
"integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==",
|
"integrity": "sha512-y9XDjCEuBp+98k+UL5dbYkh57AHU4o6cxZedOPXw3bmrZZYLQsVHguGurq7hVrPCSrQtrnz1f9dssyFr+dMXfQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
}
|
}
|
||||||
@@ -7005,7 +7058,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.34.tgz",
|
||||||
"integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==",
|
"integrity": "sha512-mKeBYvu8tcMSLhypAHBmriUFfWXKTCF/23Z4jiCoYK3UtWepkliViNLuR90V9XOyD62mUxs9p1jsrpK3CCGIzw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/reactivity": "3.5.34",
|
"@vue/reactivity": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -7015,7 +7067,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.34.tgz",
|
||||||
"integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==",
|
"integrity": "sha512-e8kZzERmCwUnBRVsgSQlAfrfU2rGoy0FFKPBXSlfEjc/O3KfA7QP0t1/2ZylrbchjmIKB4dPTd07A6WPr0eOrg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/reactivity": "3.5.34",
|
"@vue/reactivity": "3.5.34",
|
||||||
"@vue/runtime-core": "3.5.34",
|
"@vue/runtime-core": "3.5.34",
|
||||||
@@ -7027,7 +7078,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.34.tgz",
|
||||||
"integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==",
|
"integrity": "sha512-nHxmJoTrKsmrkbILRhkC9gY1G3moZbJTqCzDd7DOOzG5KH9oeJ0Unqrff5f9v0pW//jES05ZkJcNtfE8JjOIew==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-ssr": "3.5.34",
|
"@vue/compiler-ssr": "3.5.34",
|
||||||
"@vue/shared": "3.5.34"
|
"@vue/shared": "3.5.34"
|
||||||
@@ -7036,8 +7086,7 @@
|
|||||||
"@vue/shared": {
|
"@vue/shared": {
|
||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.34.tgz",
|
||||||
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA==",
|
"integrity": "sha512-24uqU4OIiX29ryC3MeWid/Xf2fa2EFRUVLb77nRhk+UrTVrh/XiGtFAFmJBAtBRbjwNdsPRP+jj/OL27Eg1NDA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@xmldom/xmldom": {
|
"@xmldom/xmldom": {
|
||||||
"version": "0.9.10",
|
"version": "0.9.10",
|
||||||
@@ -7758,8 +7807,7 @@
|
|||||||
"csstype": {
|
"csstype": {
|
||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"de-indent": {
|
"de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
@@ -8223,8 +8271,7 @@
|
|||||||
"entities": {
|
"entities": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
|
||||||
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"env-paths": {
|
"env-paths": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
@@ -8323,8 +8370,7 @@
|
|||||||
"estree-walker": {
|
"estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"extract-zip": {
|
"extract-zip": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -9091,7 +9137,6 @@
|
|||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
}
|
}
|
||||||
@@ -9213,8 +9258,7 @@
|
|||||||
"nanoid": {
|
"nanoid": {
|
||||||
"version": "3.3.12",
|
"version": "3.3.12",
|
||||||
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz",
|
"resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node-addon-api": {
|
"node-addon-api": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
@@ -9367,8 +9411,7 @@
|
|||||||
"picocolors": {
|
"picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"picomatch": {
|
"picomatch": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
@@ -9417,7 +9460,6 @@
|
|||||||
"version": "8.5.15",
|
"version": "8.5.15",
|
||||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz",
|
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz",
|
||||||
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"nanoid": "^3.3.12",
|
"nanoid": "^3.3.12",
|
||||||
"picocolors": "^1.1.1",
|
"picocolors": "^1.1.1",
|
||||||
@@ -9709,8 +9751,7 @@
|
|||||||
"source-map-js": {
|
"source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"source-map-support": {
|
"source-map-support": {
|
||||||
"version": "0.5.21",
|
"version": "0.5.21",
|
||||||
@@ -9926,7 +9967,7 @@
|
|||||||
"version": "5.3.3",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz",
|
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.3.3.tgz",
|
||||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"universalify": {
|
"universalify": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
@@ -10003,7 +10044,6 @@
|
|||||||
"version": "3.5.34",
|
"version": "3.5.34",
|
||||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.34.tgz",
|
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.34.tgz",
|
||||||
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
"integrity": "sha512-WdLBG9gm02OgJIG9axd5Hpx0TFLdzVgfG2evFFu8Rur5O/IoGc5cMjnjh3tPL6GnRGsYvUhBSKVPYVcxRKpMCA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@vue/compiler-dom": "3.5.34",
|
"@vue/compiler-dom": "3.5.34",
|
||||||
"@vue/compiler-sfc": "3.5.34",
|
"@vue/compiler-sfc": "3.5.34",
|
||||||
@@ -10019,6 +10059,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
"vue-i18n": {
|
||||||
|
"version": "9.14.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.4.tgz",
|
||||||
|
"integrity": "sha512-B934C8yUyWLT0EMud3DySrwSUJI7ZNiWYsEEz2gknTthqKiG4dzWE/WSa8AzCuSQzwBEv4HtG1jZDhgzPfWSKQ==",
|
||||||
|
"requires": {
|
||||||
|
"@intlify/core-base": "9.14.4",
|
||||||
|
"@intlify/shared": "9.14.4",
|
||||||
|
"@vue/devtools-api": "^6.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"vue-router": {
|
"vue-router": {
|
||||||
"version": "4.6.4",
|
"version": "4.6.4",
|
||||||
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
|
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
|
||||||
|
|||||||
+10
-2
@@ -17,12 +17,15 @@
|
|||||||
"predist:zip": "node scripts/check-native-dlls.js && node scripts/clean-release-artifacts.js",
|
"predist:zip": "node scripts/check-native-dlls.js && node scripts/clean-release-artifacts.js",
|
||||||
"dist": "electron-vite build && electron-builder",
|
"dist": "electron-vite build && electron-builder",
|
||||||
"dist:dir": "electron-vite build && electron-builder --dir",
|
"dist:dir": "electron-vite build && electron-builder --dir",
|
||||||
"dist:zip": "electron-vite build && electron-builder --win zip"
|
"dist:zip": "electron-vite build && electron-builder --win zip",
|
||||||
|
"prerelease": "node scripts/check-native-dlls.js && node scripts/clean-release-artifacts.js",
|
||||||
|
"release": "electron-vite build && electron-builder --win nsis"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-log": "^5.1.2",
|
"electron-log": "^5.1.2",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"koffi": "^2.9.0"
|
"koffi": "^2.9.0",
|
||||||
|
"vue-i18n": "^9.14.4"
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.cardsoon.machine",
|
"appId": "com.cardsoon.machine",
|
||||||
@@ -70,6 +73,11 @@
|
|||||||
],
|
],
|
||||||
"signAndEditExecutable": false
|
"signAndEditExecutable": false
|
||||||
},
|
},
|
||||||
|
"nsis": {
|
||||||
|
"oneClick": false,
|
||||||
|
"allowToChangeInstallationDirectory": true,
|
||||||
|
"perMachine": false
|
||||||
|
},
|
||||||
"mac": {
|
"mac": {
|
||||||
"target": [
|
"target": [
|
||||||
"dmg"
|
"dmg"
|
||||||
|
|||||||
+8
-16
@@ -1,4 +1,4 @@
|
|||||||
import { app, BrowserWindow, dialog, globalShortcut, screen } from 'electron'
|
import { app, BrowserWindow, dialog, globalShortcut } from 'electron'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
app.commandLine.appendSwitch('disable-gpu-shader-disk-cache')
|
app.commandLine.appendSwitch('disable-gpu-shader-disk-cache')
|
||||||
@@ -14,10 +14,12 @@ if (!gotSingleInstanceLock) {
|
|||||||
app.quit()
|
app.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { ensureConsoleUtf8 } from './utils/ensure-console-utf8'
|
||||||
import { suppressKnownDllStderr } from './utils/suppress-dll-stderr'
|
import { suppressKnownDllStderr } from './utils/suppress-dll-stderr'
|
||||||
import { loadAppFileConfig, applyFileConfigToStore } from './services/app-config'
|
import { loadAppFileConfig, applyFileConfigToStore } from './services/app-config'
|
||||||
import { migrateTraceConfig, setTraceWebContents } from './utils/trace-bridge'
|
import { migrateTraceConfig, setTraceWebContents } from './utils/trace-bridge'
|
||||||
|
|
||||||
|
ensureConsoleUtf8()
|
||||||
suppressKnownDllStderr()
|
suppressKnownDllStderr()
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
@@ -41,24 +43,12 @@ function focusMainWindow(): void {
|
|||||||
mainWindow.focus()
|
mainWindow.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultWindowSize(): { width: number; height: number } {
|
|
||||||
const { width: sw, height: sh } = screen.getPrimaryDisplay().workAreaSize
|
|
||||||
let w = Math.max(1280, Math.min(Math.floor(sw * 0.85), 1600))
|
|
||||||
let h = contentHeightForWidth(w)
|
|
||||||
const maxH = Math.floor(sh * 0.85)
|
|
||||||
if (h > maxH) {
|
|
||||||
h = Math.max(contentHeightForWidth(MIN_CONTENT_WIDTH), maxH)
|
|
||||||
w = Math.round((h * DESIGN_WIDTH) / DESIGN_HEIGHT)
|
|
||||||
}
|
|
||||||
return { width: w, height: h }
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWindow(): void {
|
function createWindow(): void {
|
||||||
const { width, height } = getDefaultWindowSize()
|
// 目标设备屏幕为 720x360,窗口默认 1:1 显示设计稿大小
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
useContentSize: true,
|
useContentSize: true,
|
||||||
width,
|
width: DESIGN_WIDTH,
|
||||||
height,
|
height: DESIGN_HEIGHT,
|
||||||
minWidth: MIN_CONTENT_WIDTH,
|
minWidth: MIN_CONTENT_WIDTH,
|
||||||
minHeight: contentHeightForWidth(MIN_CONTENT_WIDTH),
|
minHeight: contentHeightForWidth(MIN_CONTENT_WIDTH),
|
||||||
show: false,
|
show: false,
|
||||||
@@ -94,6 +84,8 @@ function createWindow(): void {
|
|||||||
|
|
||||||
mainWindow.on('resize', () => {
|
mainWindow.on('resize', () => {
|
||||||
if (!mainWindow) return
|
if (!mainWindow) return
|
||||||
|
// 最大化时让窗口填满屏幕,不强制 2:1 内容比例
|
||||||
|
if (mainWindow.isMaximized()) return
|
||||||
const [cw, ch] = mainWindow.getContentSize()
|
const [cw, ch] = mainWindow.getContentSize()
|
||||||
const wantH = contentHeightForWidth(cw)
|
const wantH = contentHeightForWidth(cw)
|
||||||
if (Math.abs(ch - wantH) > 2) {
|
if (Math.abs(ch - wantH) > 2) {
|
||||||
|
|||||||
@@ -107,7 +107,9 @@ export function registerIpcHandlers(): void {
|
|||||||
ribbonType: cached?.ribbonType ?? '—',
|
ribbonType: cached?.ribbonType ?? '—',
|
||||||
ribbonAmount: cached?.ribbonAmount ?? '—',
|
ribbonAmount: cached?.ribbonAmount ?? '—',
|
||||||
statusText: parsed.statusText,
|
statusText: parsed.statusText,
|
||||||
serialNo: cached?.serialNo ?? '—'
|
serialNo: cached?.serialNo ?? '—',
|
||||||
|
printerName: cached?.printerName ?? '—',
|
||||||
|
isSingleSide: cached?.isSingleSide ?? false
|
||||||
}
|
}
|
||||||
configStore.set('lastPrinterStatus', snapshot)
|
configStore.set('lastPrinterStatus', snapshot)
|
||||||
return ok({ statusText: parsed.statusText, statusCode: code })
|
return ok({ statusText: parsed.statusText, statusCode: code })
|
||||||
@@ -152,6 +154,32 @@ export function registerIpcHandlers(): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 读卡:移动卡片到读取区(SAPI_PrinterMovetousbreader)
|
||||||
|
tracedHandle('dll:printer-read-card', async () => {
|
||||||
|
try {
|
||||||
|
assertReady()
|
||||||
|
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, '读卡失败')
|
||||||
|
} catch (err) {
|
||||||
|
return fail(CS_FAIL, String(err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 退卡:移动卡片到出卡区(SAPI_PrinterMovetohopper)
|
||||||
|
tracedHandle('dll:printer-eject-card', async () => {
|
||||||
|
try {
|
||||||
|
assertReady()
|
||||||
|
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, '退卡失败')
|
||||||
|
} catch (err) {
|
||||||
|
return fail(CS_FAIL, String(err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
tracedHandle('dll:printer-error-str', async (_e, errorNo?: number) => {
|
tracedHandle('dll:printer-error-str', async (_e, errorNo?: number) => {
|
||||||
if (!mainAppState.initialized) return ok({ text: '' })
|
if (!mainAppState.initialized) return ok({ text: '' })
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export async function openDesignApp(
|
|||||||
const target = path.resolve(exePath.trim())
|
const target = path.resolve(exePath.trim())
|
||||||
const err = await shell.openPath(target)
|
const err = await shell.openPath(target)
|
||||||
if (err) {
|
if (err) {
|
||||||
return { ok: false, message: err }
|
// 用户在 UAC/系统弹窗中点击"否"或被系统拒绝时,shell.openPath 返回通用错误字符串
|
||||||
|
// 不把原始 "Failed to Open Path" 直接抛给用户,给出可读说明
|
||||||
|
return { ok: false, message: '打开设计软件失败,可能已被取消或需要管理员权限' }
|
||||||
}
|
}
|
||||||
return { ok: true }
|
return { ok: true }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ let SAPI_Init: any = null
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
let SAPI_GetPrinterInfo: any = null
|
let SAPI_GetPrinterInfo: any = null
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
let SAPI_FreePrinterInfo: any = null
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
let SAPI_GetPrinterErrorStr: any = null
|
let SAPI_GetPrinterErrorStr: any = null
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
let SAPI_RestJobEx: any = null
|
let SAPI_RestJobEx: any = null
|
||||||
@@ -77,6 +79,20 @@ function traceCall<T>(name: string, args: Record<string, unknown> | undefined, f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function freePrinterInfoPtr(ptr: number): void {
|
||||||
|
if (!ptr) return
|
||||||
|
// DLL 堆分配的缓冲区必须用 DLL 导出的释放函数;koffi.free 会触发 STATUS_HEAP_CORRUPTION
|
||||||
|
if (SAPI_FreePrinterInfo) {
|
||||||
|
try {
|
||||||
|
SAPI_FreePrinterInfo(ptr)
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('SAPI_FreePrinterInfo failed', e)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.warn('SAPI_FreePrinterInfo unavailable; printer info buffer not freed')
|
||||||
|
}
|
||||||
|
|
||||||
function readPrinterJsonFromOutPtr(len: number, outPtr: Buffer): { code: number; json?: Record<string, unknown> } {
|
function readPrinterJsonFromOutPtr(len: number, outPtr: Buffer): { code: number; json?: Record<string, unknown> } {
|
||||||
if (len <= 0) return { code: len }
|
if (len <= 0) return { code: len }
|
||||||
const ptr = koffi.decode(outPtr, 0, 'void *') as number
|
const ptr = koffi.decode(outPtr, 0, 'void *') as number
|
||||||
@@ -90,7 +106,7 @@ function readPrinterJsonFromOutPtr(len: number, outPtr: Buffer): { code: number;
|
|||||||
return { code: len }
|
return { code: len }
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
koffi.free(ptr)
|
freePrinterInfoPtr(ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +126,13 @@ function loadLibrary(): void {
|
|||||||
SAPI_GetUsbCopyState = lib.func('int __stdcall SAPI_GetUsbCopyState(_Out_ int *, _Out_ int *)')
|
SAPI_GetUsbCopyState = lib.func('int __stdcall SAPI_GetUsbCopyState(_Out_ int *, _Out_ int *)')
|
||||||
SAPI_PrinterResetprinter = lib.func('int __stdcall SAPI_PrinterResetprinter()')
|
SAPI_PrinterResetprinter = lib.func('int __stdcall SAPI_PrinterResetprinter()')
|
||||||
|
|
||||||
|
try {
|
||||||
|
SAPI_FreePrinterInfo = lib.func('void __stdcall SAPI_FreePrinterInfo(void *)')
|
||||||
|
} catch {
|
||||||
|
SAPI_FreePrinterInfo = null
|
||||||
|
log.warn('SAPI_FreePrinterInfo not in workDll')
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SAPI_UploadFile = lib.func('int __stdcall SAPI_UploadFile(str, str, str)')
|
SAPI_UploadFile = lib.func('int __stdcall SAPI_UploadFile(str, str, str)')
|
||||||
hasUploadApi = true
|
hasUploadApi = true
|
||||||
@@ -151,6 +174,15 @@ function loadLibrary(): void {
|
|||||||
emitTrace('[dll] SAPI_PrinterMovetousbreader not in workDll (optional)')
|
emitTrace('[dll] SAPI_PrinterMovetousbreader not in workDll (optional)')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SAPI_PrinterMovetohopper = lib.func('int __stdcall SAPI_PrinterMovetohopper()')
|
||||||
|
hasHopperApi = true
|
||||||
|
} catch {
|
||||||
|
SAPI_PrinterMovetohopper = null
|
||||||
|
hasHopperApi = false
|
||||||
|
emitTrace('[dll] SAPI_PrinterMovetohopper not in workDll (optional)')
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SAPI_GetPrinterCardPosition = lib.func('int __stdcall SAPI_GetPrinterCardPosition(_Out_ int *)')
|
SAPI_GetPrinterCardPosition = lib.func('int __stdcall SAPI_GetPrinterCardPosition(_Out_ int *)')
|
||||||
hasCardPositionApi = true
|
hasCardPositionApi = true
|
||||||
@@ -354,6 +386,14 @@ export function dllPrinterMoveToUsbReader(): number {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function dllPrinterMoveToHopper(): number {
|
||||||
|
return traceCall('SAPI_PrinterMovetohopper', undefined, () => {
|
||||||
|
loadLibrary()
|
||||||
|
if (!SAPI_PrinterMovetohopper) throw new Error('HOPPER_API_UNAVAILABLE')
|
||||||
|
return SAPI_PrinterMovetohopper() as number
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function dllPrinterReject(): number {
|
export function dllPrinterReject(): number {
|
||||||
return traceCall('SAPI_PrinterMovetoreject', undefined, () => {
|
return traceCall('SAPI_PrinterMovetoreject', undefined, () => {
|
||||||
loadLibrary()
|
loadLibrary()
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import koffi from 'koffi'
|
||||||
|
|
||||||
|
/** Windows 控制台默认多为 GBK,DLL 日志为 UTF-8,会导致中文乱码。 */
|
||||||
|
export function ensureConsoleUtf8(): void {
|
||||||
|
if (process.platform !== 'win32') return
|
||||||
|
try {
|
||||||
|
const kernel32 = koffi.load('kernel32.dll')
|
||||||
|
const SetConsoleOutputCP = kernel32.func('bool __stdcall SetConsoleOutputCP(uint32)')
|
||||||
|
const SetConsoleCP = kernel32.func('bool __stdcall SetConsoleCP(uint32)')
|
||||||
|
SetConsoleOutputCP(65001)
|
||||||
|
SetConsoleCP(65001)
|
||||||
|
} catch {
|
||||||
|
/* ignore: no console or API unavailable */
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (process.stdout.setDefaultEncoding) process.stdout.setDefaultEncoding('utf8')
|
||||||
|
if (process.stderr.setDefaultEncoding) process.stderr.setDefaultEncoding('utf8')
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,16 @@ export async function dllPrinterReject(): Promise<IpcResult> {
|
|||||||
return api().invoke('dll:printer-reject') as Promise<IpcResult>
|
return api().invoke('dll:printer-reject') as Promise<IpcResult>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 读卡:移动卡片到读取区(SAPI_PrinterMovetousbreader) */
|
||||||
|
export async function dllPrinterReadCard(): Promise<IpcResult> {
|
||||||
|
return api().invoke('dll:printer-read-card') as Promise<IpcResult>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 退卡:移动卡片到出卡区(SAPI_PrinterMovetohopper) */
|
||||||
|
export async function dllPrinterEjectCard(): Promise<IpcResult> {
|
||||||
|
return api().invoke('dll:printer-eject-card') as Promise<IpcResult>
|
||||||
|
}
|
||||||
|
|
||||||
export async function dllRejectAvailable(): Promise<IpcResult<{ available: boolean }>> {
|
export async function dllRejectAvailable(): Promise<IpcResult<{ available: boolean }>> {
|
||||||
return api().invoke('dll:reject-available') as Promise<IpcResult<{ available: boolean }>>
|
return api().invoke('dll:reject-available') as Promise<IpcResult<{ available: boolean }>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ export const ICON_NAMES = [
|
|||||||
'plus',
|
'plus',
|
||||||
'exchange',
|
'exchange',
|
||||||
'stop',
|
'stop',
|
||||||
'warning'
|
'warning',
|
||||||
|
'id-card',
|
||||||
|
'eject'
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export type IconName = (typeof ICON_NAMES)[number]
|
export type IconName = (typeof ICON_NAMES)[number]
|
||||||
@@ -34,5 +36,7 @@ export const ICON_FA_CLASS: Record<IconName, string> = {
|
|||||||
plus: 'fas fa-plus',
|
plus: 'fas fa-plus',
|
||||||
exchange: 'fas fa-exchange-alt',
|
exchange: 'fas fa-exchange-alt',
|
||||||
stop: 'fas fa-stop',
|
stop: 'fas fa-stop',
|
||||||
warning: 'fas fa-exclamation-triangle'
|
warning: 'fas fa-exclamation-triangle',
|
||||||
|
'id-card': 'fas fa-id-card',
|
||||||
|
eject: 'fas fa-eject'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<footer class="c-footer">
|
<footer class="c-footer">
|
||||||
<div>版本V1.0</div>
|
<div class="c-footer__version">{{ t('footer.version', { version: appVersion }) }}</div>
|
||||||
<div>www.cardsoon.com</div>
|
<div>{{ t('footer.website') }}</div>
|
||||||
<div>版权所有 © 2026 卡树科技</div>
|
<div>{{ t('footer.copyright') }}</div>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
// 构建时由 electron.vite.config.ts 从 package.json version 注入
|
||||||
|
const appVersion = __APP_VERSION__
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -5,16 +5,24 @@
|
|||||||
<div class="c-header__center">
|
<div class="c-header__center">
|
||||||
<div v-if="mode" class="c-mode-badge c-mode-badge--home">{{ mode }}</div>
|
<div v-if="mode" class="c-mode-badge c-mode-badge--home">{{ mode }}</div>
|
||||||
<div class="c-status-capsule">
|
<div class="c-status-capsule">
|
||||||
<span>色带: <b>{{ status.ribbonType }}</b></span>
|
<span>{{ t('header.ribbon') }}: <b>{{ status.ribbonType }}</b></span>
|
||||||
<span>余量: <b>{{ status.ribbonAmount }}</b></span>
|
<span>{{ t('header.ribbonAmount') }}: <b>{{ status.ribbonAmount }}</b></span>
|
||||||
<span
|
<span>{{ t('header.status') }}: <b :class="statusTone">{{ displayStatusText }}</b></span>
|
||||||
>状态: <b :class="statusTone">{{ status.statusText }}</b></span
|
<span>{{ t('header.serialNo') }}: <b>{{ status.serialNo }}</b></span>
|
||||||
>
|
|
||||||
<span>序列号: <b>{{ status.serialNo }}</b></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-header__actions-slot">
|
<div class="c-header__actions-slot">
|
||||||
<div class="c-header-actions">
|
<div class="c-header-actions">
|
||||||
|
<select
|
||||||
|
class="c-lang-select"
|
||||||
|
:value="currentLocale"
|
||||||
|
@change="onLocaleChange"
|
||||||
|
:title="t('common.select')"
|
||||||
|
>
|
||||||
|
<option v-for="opt in LOCALE_OPTIONS" :key="opt.value" :value="opt.value">
|
||||||
|
{{ opt.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,16 +32,64 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useConfigStore } from '@/stores/config'
|
import { useConfigStore } from '@/stores/config'
|
||||||
|
import { LOCALE_OPTIONS, persistLocale, type AppLocale } from '@/i18n'
|
||||||
|
|
||||||
defineProps<{ mode?: string }>()
|
defineProps<{ mode?: string }>()
|
||||||
|
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
const status = computed(() => configStore.printer)
|
const status = computed(() => configStore.printer)
|
||||||
|
const { t, locale } = useI18n()
|
||||||
|
|
||||||
|
const currentLocale = computed(() => locale.value)
|
||||||
|
|
||||||
|
function onLocaleChange(e: Event): void {
|
||||||
|
const val = (e.target as HTMLSelectElement).value as AppLocale
|
||||||
|
locale.value = val
|
||||||
|
persistLocale(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打印机状态文本来自主进程(中文),在前端按已知值做多语言映射
|
||||||
|
const PRINTER_STATUS_MAP: Record<string, string> = {
|
||||||
|
空闲: 'printerStatus.idle',
|
||||||
|
忙碌: 'printerStatus.busy',
|
||||||
|
正在打印: 'printerStatus.printing',
|
||||||
|
未连接打印机: 'printerStatus.notConnected',
|
||||||
|
未初始化: 'printerStatus.notInitialized',
|
||||||
|
初始化失败: 'printerStatus.initFailed',
|
||||||
|
就绪: 'printerStatus.ready'
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayStatusText = computed(() => {
|
||||||
|
const raw = status.value.statusText
|
||||||
|
const key = PRINTER_STATUS_MAP[raw]
|
||||||
|
return key ? t(key) : raw
|
||||||
|
})
|
||||||
|
|
||||||
const statusTone = computed(() => {
|
const statusTone = computed(() => {
|
||||||
const t = status.value.statusText
|
const toneMap: Record<string, string> = {
|
||||||
if (t.includes('未初始化') || t.includes('未连接') || t.includes('失败')) return 'c-status-warn'
|
[t('printerStatus.notInitialized')]: 'c-status-warn',
|
||||||
return ''
|
[t('printerStatus.notConnected')]: 'c-status-warn',
|
||||||
|
[t('printerStatus.initFailed')]: 'c-status-warn'
|
||||||
|
}
|
||||||
|
return toneMap[displayStatusText.value] || ''
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.c-lang-select {
|
||||||
|
height: 28px;
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #fff;
|
||||||
|
color: #303133;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 6px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.c-lang-select:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -4,69 +4,69 @@
|
|||||||
<div v-if="visible" class="npd-mask" @mousedown.self="onCancel">
|
<div v-if="visible" class="npd-mask" @mousedown.self="onCancel">
|
||||||
<div class="npd-dialog" role="dialog" aria-modal="true" aria-labelledby="npd-title">
|
<div class="npd-dialog" role="dialog" aria-modal="true" aria-labelledby="npd-title">
|
||||||
<div class="npd-header">
|
<div class="npd-header">
|
||||||
<span id="npd-title" class="npd-title">添加网络位置</span>
|
<span id="npd-title" class="npd-title">{{ t('networkDialog.title') }}</span>
|
||||||
<button type="button" class="npd-close" aria-label="关闭" @click="onCancel">×</button>
|
<button type="button" class="npd-close" :aria-label="t('common.close')" @click="onCancel">×</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="npd-body">
|
<div class="npd-body">
|
||||||
<p class="npd-hint">凭据用于访问已添加的映射盘或网络共享路径,请确认主机可访问且账号有效。</p>
|
<p class="npd-hint">{{ t('networkDialog.hint') }}</p>
|
||||||
|
|
||||||
<label class="npd-field">
|
<label class="npd-field">
|
||||||
<span class="npd-label">主机(IP 或主机名)<span class="npd-req">*</span></span>
|
<span class="npd-label">{{ t('networkDialog.host') }}<span class="npd-req">*</span></span>
|
||||||
<input
|
<input
|
||||||
v-model.trim="host"
|
v-model.trim="host"
|
||||||
type="text"
|
type="text"
|
||||||
class="c-input"
|
class="c-input"
|
||||||
placeholder="例如 192.168.1.100 或 nas-server"
|
:placeholder="t('networkDialog.hostPlaceholder')"
|
||||||
:class="{ 'is-invalid': touched && !hostValid }"
|
:class="{ 'is-invalid': touched && !hostValid }"
|
||||||
@blur="touched = true"
|
@blur="touched = true"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="npd-field">
|
<label class="npd-field">
|
||||||
<span class="npd-label">共享名(可选)</span>
|
<span class="npd-label">{{ t('networkDialog.share') }}</span>
|
||||||
<input
|
<input
|
||||||
v-model.trim="share"
|
v-model.trim="share"
|
||||||
type="text"
|
type="text"
|
||||||
class="c-input"
|
class="c-input"
|
||||||
placeholder="例如 share,留空表示只挂载到根"
|
:placeholder="t('networkDialog.sharePlaceholder')"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="npd-field">
|
<label class="npd-field">
|
||||||
<span class="npd-label">用户名<span class="npd-req">*</span></span>
|
<span class="npd-label">{{ t('networkDialog.userName') }}<span class="npd-req">*</span></span>
|
||||||
<input
|
<input
|
||||||
v-model.trim="userName"
|
v-model.trim="userName"
|
||||||
type="text"
|
type="text"
|
||||||
class="c-input"
|
class="c-input"
|
||||||
placeholder="例如 admin"
|
:placeholder="t('networkDialog.userNamePlaceholder')"
|
||||||
:class="{ 'is-invalid': touched && !userNameValid }"
|
:class="{ 'is-invalid': touched && !userNameValid }"
|
||||||
@blur="touched = true"
|
@blur="touched = true"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="npd-field">
|
<label class="npd-field">
|
||||||
<span class="npd-label">密码<span class="npd-req">*</span></span>
|
<span class="npd-label">{{ t('networkDialog.password') }}<span class="npd-req">*</span></span>
|
||||||
<input
|
<input
|
||||||
v-model="password"
|
v-model="password"
|
||||||
type="password"
|
type="password"
|
||||||
class="c-input"
|
class="c-input"
|
||||||
placeholder="请输入密码"
|
:placeholder="t('networkDialog.errPassword')"
|
||||||
:class="{ 'is-invalid': touched && !passwordValid }"
|
:class="{ 'is-invalid': touched && !passwordValid }"
|
||||||
@blur="touched = true"
|
@blur="touched = true"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div v-if="previewUrl" class="npd-preview">
|
<div v-if="previewUrl" class="npd-preview">
|
||||||
<span class="npd-preview-label">目标 UNC</span>
|
<span class="npd-preview-label">{{ t('networkDialog.targetUNC') }}</span>
|
||||||
<code class="npd-preview-path">{{ previewUrl }}</code>
|
<code class="npd-preview-path">{{ previewUrl }}</code>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p v-if="touched && errorText" class="npd-error">{{ errorText }}</p>
|
<p v-if="touched && errorText" class="npd-error">{{ errorText }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="npd-footer">
|
<div class="npd-footer">
|
||||||
<button type="button" class="c-button-cs" @click="onCancel">取消</button>
|
<button type="button" class="c-button-cs" @click="onCancel">{{ t('common.cancel') }}</button>
|
||||||
<button type="button" class="c-button-cs npd-primary" :disabled="!canConfirm" @click="onConfirm">
|
<button type="button" class="c-button-cs npd-primary" :disabled="!canConfirm" @click="onConfirm">
|
||||||
确定
|
{{ t('common.confirm') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,6 +77,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { buildNetworkUrl } from '@shared/network-host'
|
import { buildNetworkUrl } from '@shared/network-host'
|
||||||
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
||||||
|
|
||||||
@@ -92,6 +93,7 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const netStore = useNetworkAuthStore()
|
const netStore = useNetworkAuthStore()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const host = ref('')
|
const host = ref('')
|
||||||
const share = ref('')
|
const share = ref('')
|
||||||
@@ -109,9 +111,9 @@ const canConfirm = computed(() => hostValid.value && userNameValid.value && pass
|
|||||||
const previewUrl = computed(() => (hostValid.value ? buildNetworkUrl(host.value, share.value) : ''))
|
const previewUrl = computed(() => (hostValid.value ? buildNetworkUrl(host.value, share.value) : ''))
|
||||||
|
|
||||||
const errorText = computed(() => {
|
const errorText = computed(() => {
|
||||||
if (!hostValid.value) return '主机名/IP 不合法'
|
if (!hostValid.value) return t('networkDialog.errHostInvalid')
|
||||||
if (!userNameValid.value) return '请输入用户名'
|
if (!userNameValid.value) return t('networkDialog.errUserName')
|
||||||
if (!passwordValid.value) return '请输入密码'
|
if (!passwordValid.value) return t('networkDialog.errPassword')
|
||||||
return ''
|
return ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@@ -23,20 +24,22 @@ const props = withDefaults(
|
|||||||
{ activeStep: 2, mode: 'running', variant: 'distribute' }
|
{ activeStep: 2, mode: 'running', variant: 'distribute' }
|
||||||
)
|
)
|
||||||
|
|
||||||
const distributeSteps = [
|
const { t } = useI18n()
|
||||||
{ key: 'prep', label: '任务准备' },
|
|
||||||
{ key: 'copy', label: '拷贝数据' },
|
|
||||||
{ key: 'print', label: '打印卡片' },
|
|
||||||
{ key: 'done', label: '完成' }
|
|
||||||
] as const
|
|
||||||
|
|
||||||
const collectSteps = [
|
const distributeSteps = computed(() => [
|
||||||
{ key: 'prep', label: '任务准备' },
|
{ key: 'prep', label: t('workflow.taskPrep') },
|
||||||
{ key: 'copy', label: '拷贝数据' },
|
{ key: 'copy', label: t('workflow.copyData') },
|
||||||
{ key: 'done', label: '完成' }
|
{ key: 'print', label: t('workflow.printCard') },
|
||||||
] as const
|
{ key: 'done', label: t('workflow.complete') }
|
||||||
|
])
|
||||||
|
|
||||||
const steps = computed(() => (props.variant === 'collect' ? collectSteps : distributeSteps))
|
const collectSteps = computed(() => [
|
||||||
|
{ key: 'prep', label: t('workflow.taskPrep') },
|
||||||
|
{ key: 'copy', label: t('workflow.copyData') },
|
||||||
|
{ key: 'done', label: t('workflow.complete') }
|
||||||
|
])
|
||||||
|
|
||||||
|
const steps = computed(() => (props.variant === 'collect' ? collectSteps.value : distributeSteps.value))
|
||||||
|
|
||||||
const failedStep = computed(() => props.failedStep ?? (props.mode === 'failed' ? 3 : -1))
|
const failedStep = computed(() => props.failedStep ?? (props.mode === 'failed' ? 3 : -1))
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { onMounted } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
|
import i18n from '@/i18n'
|
||||||
import { notify } from '@/composables/useNotify'
|
import { notify } from '@/composables/useNotify'
|
||||||
import { refreshPrinterAfterInit } from '@/composables/usePrinterStatus'
|
import { refreshPrinterAfterInit } from '@/composables/usePrinterStatus'
|
||||||
import { configGet, dllInit, dllRejectAvailable } from '@/api/cardsoon'
|
import { configGet, dllInit, dllRejectAvailable } from '@/api/cardsoon'
|
||||||
import { useAppStore } from '@/stores/app'
|
import { useAppStore } from '@/stores/app'
|
||||||
import { useConfigStore } from '@/stores/config'
|
import { useConfigStore } from '@/stores/config'
|
||||||
|
|
||||||
|
function t(key: string): string {
|
||||||
|
return i18n.global.t(key)
|
||||||
|
}
|
||||||
|
|
||||||
let bootstrapped = false
|
let bootstrapped = false
|
||||||
|
|
||||||
function placeholderStatus(configStore: ReturnType<typeof useConfigStore>, text: string): void {
|
function placeholderStatus(configStore: ReturnType<typeof useConfigStore>, text: string): void {
|
||||||
@@ -41,7 +46,7 @@ export function useAppBootstrap(): void {
|
|||||||
const cfg = await hydrateFromConfig()
|
const cfg = await hydrateFromConfig()
|
||||||
if (cfg.ok && cfg.data?.dllInitialized) {
|
if (cfg.ok && cfg.data?.dllInitialized) {
|
||||||
appStore.setInitialized(true)
|
appStore.setInitialized(true)
|
||||||
placeholderStatus(configStore, '就绪')
|
placeholderStatus(configStore, t('printerStatus.ready'))
|
||||||
await syncRejectApi()
|
await syncRejectApi()
|
||||||
window.setTimeout(() => void refreshPrinterAfterInit(), 1500)
|
window.setTimeout(() => void refreshPrinterAfterInit(), 1500)
|
||||||
return
|
return
|
||||||
@@ -51,16 +56,16 @@ export function useAppBootstrap(): void {
|
|||||||
configStore.setSharedDir(sharedDir)
|
configStore.setSharedDir(sharedDir)
|
||||||
const init = await dllInit({ sharedDir })
|
const init = await dllInit({ sharedDir })
|
||||||
if (!init.ok) {
|
if (!init.ok) {
|
||||||
appStore.setInitialized(false, init.message || 'Init 失败')
|
appStore.setInitialized(false, init.message || t('printerStatus.initFailed'))
|
||||||
configStore.setPrinter({ ...configStore.printer, statusText: '初始化失败' })
|
configStore.setPrinter({ ...configStore.printer, statusText: t('printerStatus.initFailed') })
|
||||||
notify.error(init.message || '初始化失败,请检查任务目录权限')
|
notify.error(init.message || t('notify.initFailedHint'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
appStore.setInitialized(true)
|
appStore.setInitialized(true)
|
||||||
const initMeta = init.data as { warning?: string } | undefined
|
const initMeta = init.data as { warning?: string } | undefined
|
||||||
if (initMeta?.warning) notify.warning(initMeta.warning)
|
if (initMeta?.warning) notify.warning(initMeta.warning)
|
||||||
placeholderStatus(configStore, initMeta?.warning ? '未连接打印机' : '就绪')
|
placeholderStatus(configStore, initMeta?.warning ? t('printerStatus.notConnected') : t('printerStatus.ready'))
|
||||||
await syncRejectApi()
|
await syncRejectApi()
|
||||||
window.setTimeout(() => void refreshPrinterAfterInit(), 1500)
|
window.setTimeout(() => void refreshPrinterAfterInit(), 1500)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
import i18n from '@/i18n'
|
||||||
import { useToastStore, type ToastType } from '@/stores/toast'
|
import { useToastStore, type ToastType } from '@/stores/toast'
|
||||||
|
|
||||||
|
function t(key: string, named?: Record<string, unknown>): string {
|
||||||
|
return i18n.global.t(key, named ?? {})
|
||||||
|
}
|
||||||
|
|
||||||
function push(type: ToastType, message: string, durationMs = 4500): void {
|
function push(type: ToastType, message: string, durationMs = 4500): void {
|
||||||
useToastStore().push(type, message, durationMs)
|
useToastStore().push(type, message, durationMs)
|
||||||
}
|
}
|
||||||
@@ -11,9 +16,11 @@ export const notify = {
|
|||||||
info: (message: string, durationMs?: number) => push('info', message, durationMs)
|
info: (message: string, durationMs?: number) => push('info', message, durationMs)
|
||||||
}
|
}
|
||||||
|
|
||||||
const INIT_HINT = '系统未就绪,请重启应用或检查打印机与任务目录'
|
|
||||||
|
|
||||||
/** 未初始化等业务拦截时的统一提示 */
|
/** 未初始化等业务拦截时的统一提示 */
|
||||||
export function notifyRequireInit(action?: string): void {
|
export function notifyRequireInit(action?: string): void {
|
||||||
notify.warning(action ? `系统未就绪,无法${action}` : INIT_HINT)
|
if (action) {
|
||||||
|
notify.warning(t('notify.notReadyAction', { action }))
|
||||||
|
} else {
|
||||||
|
notify.warning(t('notify.notReady'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ export async function refreshPrinterInfo(): Promise<void> {
|
|||||||
...store.printer,
|
...store.printer,
|
||||||
ribbonType: parsed.ribbonType,
|
ribbonType: parsed.ribbonType,
|
||||||
ribbonAmount: parsed.ribbonAmount,
|
ribbonAmount: parsed.ribbonAmount,
|
||||||
serialNo: parsed.serialNo
|
serialNo: parsed.serialNo,
|
||||||
|
printerName: parsed.printerName,
|
||||||
|
isSingleSide: parsed.isSingleSide
|
||||||
})
|
})
|
||||||
} catch {
|
} catch {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
|
|||||||
@@ -1,28 +1,40 @@
|
|||||||
|
import type { ComposerTranslation } from 'vue-i18n'
|
||||||
|
|
||||||
export interface SelectOption {
|
export interface SelectOption {
|
||||||
label: string
|
label: string
|
||||||
value: string | number
|
value: string | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const COPY_TYPE_OPTIONS: SelectOption[] = [
|
type T = ComposerTranslation
|
||||||
{ label: '文件拷贝', value: 0 },
|
|
||||||
{ label: '镜像刻录', value: 1 }
|
|
||||||
]
|
|
||||||
|
|
||||||
export const FORMAT_TYPE_OPTIONS: SelectOption[] = [
|
export function copyTypeOptions(t: T): SelectOption[] {
|
||||||
{ label: '不格式化', value: 'none' },
|
return [
|
||||||
{ label: 'Fat32', value: 'fat32' },
|
{ label: t('copyType.fileCopy'), value: 0 },
|
||||||
{ label: 'exFat', value: 'exfat' },
|
{ label: t('copyType.imageBurn'), value: 1 }
|
||||||
{ label: 'NTFS', value: 'ntfs' }
|
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
||||||
export const PRIORITY_OPTIONS: SelectOption[] = [
|
export function formatTypeOptions(t: T): SelectOption[] {
|
||||||
{ label: '低', value: 'low' },
|
return [
|
||||||
{ label: '中', value: 'mid' },
|
{ label: t('formatType.none'), value: 'none' },
|
||||||
{ label: '高', value: 'high' }
|
{ label: t('formatType.fat32'), value: 'fat32' },
|
||||||
|
{ label: t('formatType.exfat'), value: 'exfat' },
|
||||||
|
{ label: t('formatType.ntfs'), value: 'ntfs' }
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|
||||||
export const RIBBON_TYPE_OPTIONS: SelectOption[] = [
|
export function priorityOptions(t: T): SelectOption[] {
|
||||||
{ label: '任何', value: 'any' },
|
return [
|
||||||
|
{ label: t('common.low'), value: 'low' },
|
||||||
|
{ label: t('common.mid'), value: 'mid' },
|
||||||
|
{ label: t('common.high'), value: 'high' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ribbonTypeOptions(t: T): SelectOption[] {
|
||||||
|
return [
|
||||||
|
{ label: t('common.any'), value: 'any' },
|
||||||
{ label: 'YMCKO', value: 'YMCKO' },
|
{ label: 'YMCKO', value: 'YMCKO' },
|
||||||
{ label: 'YMCK', value: 'YMCK' }
|
{ label: 'YMCK', value: 'YMCK' }
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -14,6 +14,8 @@ interface CardsoonApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
/** 构建时由 electron.vite.config.ts 从 package.json version 注入 */
|
||||||
|
const __APP_VERSION__: string
|
||||||
interface Window {
|
interface Window {
|
||||||
cardsoonApi: CardsoonApi
|
cardsoonApi: CardsoonApi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import zhCN from './locales/zh-CN'
|
||||||
|
import enUS from './locales/en-US'
|
||||||
|
import zhTW from './locales/zh-TW'
|
||||||
|
|
||||||
|
export type AppLocale = 'zh-CN' | 'en-US' | 'zh-TW'
|
||||||
|
|
||||||
|
export const LOCALE_OPTIONS: { value: AppLocale; label: string }[] = [
|
||||||
|
{ value: 'zh-CN', label: '中文' },
|
||||||
|
{ value: 'en-US', label: 'English' },
|
||||||
|
{ value: 'zh-TW', label: '繁體中文' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'app-locale'
|
||||||
|
|
||||||
|
function detectInitialLocale(): AppLocale {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(STORAGE_KEY) as AppLocale | null
|
||||||
|
if (saved && LOCALE_OPTIONS.some((o) => o.value === saved)) return saved
|
||||||
|
} catch {
|
||||||
|
/* localStorage may be unavailable */
|
||||||
|
}
|
||||||
|
return 'zh-CN'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function persistLocale(locale: AppLocale): void {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(STORAGE_KEY, locale)
|
||||||
|
} catch {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const i18n = createI18n({
|
||||||
|
legacy: false,
|
||||||
|
locale: detectInitialLocale(),
|
||||||
|
fallbackLocale: 'zh-CN',
|
||||||
|
messages: {
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
'en-US': enUS,
|
||||||
|
'zh-TW': zhTW
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default i18n
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
export default {
|
||||||
|
common: {
|
||||||
|
cancel: 'Cancel',
|
||||||
|
confirm: 'OK',
|
||||||
|
close: 'Close',
|
||||||
|
addPath: 'Add Path',
|
||||||
|
clear: 'Clear',
|
||||||
|
submit: 'Submit',
|
||||||
|
home: 'Home',
|
||||||
|
stop: 'Stop',
|
||||||
|
back: 'Back',
|
||||||
|
reset: 'Reset',
|
||||||
|
select: 'Select',
|
||||||
|
resetPrinter: 'Reset Printer',
|
||||||
|
discardCard: 'Discard Card',
|
||||||
|
templateDesign: 'Template Design',
|
||||||
|
low: 'Low',
|
||||||
|
mid: 'Medium',
|
||||||
|
high: 'High',
|
||||||
|
any: 'Any'
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
modeHome: 'CardSoon Data Card Printer',
|
||||||
|
modeDistribute: 'Data Distribution Mode',
|
||||||
|
modeCollect: 'Data Collection Mode',
|
||||||
|
ribbon: 'Ribbon',
|
||||||
|
ribbonAmount: 'Remaining',
|
||||||
|
status: 'Status',
|
||||||
|
serialNo: 'Serial No.'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
version: 'Version v{version}',
|
||||||
|
website: 'www.cardsoon.com',
|
||||||
|
copyright: 'Copyright © 2026 CardSoon Technology'
|
||||||
|
},
|
||||||
|
home: {
|
||||||
|
toolsTitle: 'Tools',
|
||||||
|
tasksTitle: 'Tasks',
|
||||||
|
dataDistribute: 'Data Distribution',
|
||||||
|
dataDistributeDesc: 'Distribute data to print cards',
|
||||||
|
dataCollect: 'Data Collection',
|
||||||
|
dataCollectDesc: 'Collect data from cards'
|
||||||
|
},
|
||||||
|
distributeConfig: {
|
||||||
|
pathConfig: 'Path Configuration',
|
||||||
|
addNetworkLocation: 'Add Network Location',
|
||||||
|
pathHint: 'The system will copy all items under this directory, excluding the folder itself',
|
||||||
|
netCredConfigured: 'Network credentials configured: {hosts}',
|
||||||
|
volumeLabel: 'Volume Label',
|
||||||
|
copyType: 'Copy Type',
|
||||||
|
formatType: 'Format Type',
|
||||||
|
dongle: 'Dongle',
|
||||||
|
dongleHint: '(101 = unlimited)',
|
||||||
|
dongleAuthLabel: 'Auth Code',
|
||||||
|
donglePassword: 'Enter authorization code',
|
||||||
|
templatePreview: 'Label Preview',
|
||||||
|
addLabel: 'Add Label',
|
||||||
|
doubleSide: 'Double-sided',
|
||||||
|
singleSideHint: 'Single-side printer detected, please select a side (front or back)',
|
||||||
|
frontSide: 'Front',
|
||||||
|
backSide: 'Back',
|
||||||
|
imageFieldNotSelected: 'No image selected',
|
||||||
|
templateLoaded: 'Label template loaded',
|
||||||
|
templateNoPreview: 'Template opened, but no previewable content found',
|
||||||
|
cleared: 'Cleared, restored to initial state',
|
||||||
|
netCredSaved: 'Network credentials saved',
|
||||||
|
loadProgress: 'Loaded: {loaded} GB / {total} GB ({percent}%)',
|
||||||
|
calculating: 'Calculating…',
|
||||||
|
sizeUnknown: 'Size unknown',
|
||||||
|
pathInvalid: 'Invalid path',
|
||||||
|
pathReady: '{size} · Pending copy',
|
||||||
|
pathPattern: '{dir}\\*.*'
|
||||||
|
},
|
||||||
|
copyType: {
|
||||||
|
fileCopy: 'File Copy',
|
||||||
|
imageBurn: 'Image Burn'
|
||||||
|
},
|
||||||
|
formatType: {
|
||||||
|
none: 'No Format',
|
||||||
|
fat32: 'Fat32',
|
||||||
|
exfat: 'exFat',
|
||||||
|
ntfs: 'NTFS'
|
||||||
|
},
|
||||||
|
dataCollect: {
|
||||||
|
importPath: 'Data Import Path',
|
||||||
|
cardOutput: 'Card Output Direction',
|
||||||
|
forwardOutput: 'Forward Output',
|
||||||
|
backwardOutput: 'Backward Output',
|
||||||
|
noDirSelected: 'No directory selected'
|
||||||
|
},
|
||||||
|
running: {
|
||||||
|
taskFailed: 'Task Failed',
|
||||||
|
taskCompleted: 'Task Completed',
|
||||||
|
collectCompleted: 'Collection Completed',
|
||||||
|
dataCollecting: 'Collecting Data',
|
||||||
|
taskRunning: 'Task Running',
|
||||||
|
waitingCard: 'Waiting for Card',
|
||||||
|
failedSubCollect: 'Please check the reader and card, insert a backup card to retry',
|
||||||
|
failedSubDistribute: 'Please check device issues, insert a backup card to retry',
|
||||||
|
completedSubCollect: 'Click Back, or insert a backup card to continue',
|
||||||
|
completedSubDistribute: 'Click Back, or insert a backup card to start next',
|
||||||
|
collectingHint: 'Reading from card and writing to import directory',
|
||||||
|
waitCardHint: 'Please insert a data card, the task will run automatically',
|
||||||
|
runningHint: 'Task in progress, please wait',
|
||||||
|
progressCounter: 'Task completed {success} times, with {fail} failures.'
|
||||||
|
},
|
||||||
|
networkDialog: {
|
||||||
|
title: 'Add Network Location',
|
||||||
|
hint: 'Credentials are used to access mapped drives or network shares. Please confirm the host is accessible and the account is valid.',
|
||||||
|
host: 'Host (IP or Hostname)',
|
||||||
|
share: 'Share Name (optional)',
|
||||||
|
userName: 'Username',
|
||||||
|
password: 'Password',
|
||||||
|
targetUNC: 'Target UNC',
|
||||||
|
hostPlaceholder: 'e.g. 192.168.1.100 or nas-server',
|
||||||
|
sharePlaceholder: 'e.g. share, leave empty to mount root only',
|
||||||
|
userNamePlaceholder: 'e.g. admin',
|
||||||
|
errHostInvalid: 'Invalid hostname/IP',
|
||||||
|
errUserName: 'Please enter username',
|
||||||
|
errPassword: 'Please enter password'
|
||||||
|
},
|
||||||
|
workflow: {
|
||||||
|
taskPrep: 'Task Preparation',
|
||||||
|
copyData: 'Copy Data',
|
||||||
|
printCard: 'Print Card',
|
||||||
|
complete: 'Complete'
|
||||||
|
},
|
||||||
|
notify: {
|
||||||
|
resetSent: 'Reset command sent',
|
||||||
|
resetFailed: 'Reset failed',
|
||||||
|
rejectUnavailable: 'Discard card API not supported in current environment',
|
||||||
|
cardRejected: 'Card discarded',
|
||||||
|
readCardSent: 'Card moved to reader',
|
||||||
|
readCardFailed: 'Read card failed',
|
||||||
|
ejectCardSent: 'Card ejected to outlet',
|
||||||
|
ejectCardFailed: 'Eject card failed',
|
||||||
|
operationFailed: 'Operation failed',
|
||||||
|
designStarted: 'Design app started',
|
||||||
|
designFailed: 'Failed to open design app, please check cardsoon.config.json',
|
||||||
|
designCancelled: 'Failed to open design app, may have been cancelled or requires admin privileges',
|
||||||
|
notReady: 'System not ready, please restart the app or check printer and task directory',
|
||||||
|
notReadyAction: 'System not ready, cannot {action}',
|
||||||
|
stopDistribute: 'Please stop the data distribution task first',
|
||||||
|
usbCollecting: 'USB collection in progress, please wait',
|
||||||
|
usbCollectInProgress: 'Data collection in progress, please stop it on the task page first',
|
||||||
|
usbCollectComplete: 'USB collection completed',
|
||||||
|
pathOpenFailed: 'Failed to open directory picker',
|
||||||
|
templateOpenFailed: 'Failed to open template picker',
|
||||||
|
templateParseFailed: 'Template parsing failed',
|
||||||
|
dirInvalid: 'Import directory invalid, cannot continue',
|
||||||
|
resubmitCollectFailed: 'Failed to resubmit collection task',
|
||||||
|
resubmitFailed: 'Resubmit failed',
|
||||||
|
pollStartFailed: 'Failed to start task polling',
|
||||||
|
pollMonitorFailed: 'Cannot monitor card position, please click Back or resubmit manually',
|
||||||
|
queryJobFailed: 'Query task failed: {code}',
|
||||||
|
queryUsbFailed: 'Query USB task failed: {code}',
|
||||||
|
cancelIssue: 'Issue occurred while cancelling task',
|
||||||
|
selectDirFirst: 'Please select a data import directory first',
|
||||||
|
selectDirFirstShort: 'Please select a data import directory first',
|
||||||
|
startUsbFailed: 'Failed to start USB collection',
|
||||||
|
usbWaitComplete: 'Please wait for USB collection to complete',
|
||||||
|
cannotEnterRunning: 'Cannot enter running page, task cancelled',
|
||||||
|
initFailedHint: 'Initialization failed, please check task directory permissions'
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
noTask: 'Please configure a copy path or print template',
|
||||||
|
invalidTemplate: 'Please select a .soon template',
|
||||||
|
pathEmpty: 'Path cannot be empty',
|
||||||
|
pathNotExist: 'Path does not exist: {paths}',
|
||||||
|
noFilesToCopy: 'No files to copy under the path',
|
||||||
|
netCredMissing: 'Please configure network location credentials first: {host}',
|
||||||
|
templateNotExist: 'Template file does not exist',
|
||||||
|
printFlagMismatch: 'Print side mismatch, please reselect',
|
||||||
|
singleSideMustPick: 'Single-side printer, please select front or back',
|
||||||
|
dongleAuthRequired: 'Please enter authorization code',
|
||||||
|
dongleCountInvalid: 'Dongle count must be 0 or 1-101',
|
||||||
|
createJobFailed: 'Failed to create task',
|
||||||
|
csvGenFailed: 'Failed to generate print variable CSV'
|
||||||
|
},
|
||||||
|
printerStatus: {
|
||||||
|
idle: 'Idle',
|
||||||
|
busy: 'Busy',
|
||||||
|
printing: 'Printing',
|
||||||
|
notConnected: 'Printer not connected',
|
||||||
|
notInitialized: 'Not initialized',
|
||||||
|
initFailed: 'Initialization failed',
|
||||||
|
ready: 'Ready',
|
||||||
|
unknown: '—'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
export default {
|
||||||
|
common: {
|
||||||
|
cancel: '取消',
|
||||||
|
confirm: '确定',
|
||||||
|
close: '关闭',
|
||||||
|
addPath: '添加路径',
|
||||||
|
clear: '清空',
|
||||||
|
submit: '提交',
|
||||||
|
home: '首页',
|
||||||
|
stop: '停止',
|
||||||
|
back: '返回',
|
||||||
|
reset: '重置',
|
||||||
|
select: '选择',
|
||||||
|
resetPrinter: '重置打印机',
|
||||||
|
discardCard: '废弃卡片',
|
||||||
|
templateDesign: '模板设计',
|
||||||
|
low: '低',
|
||||||
|
mid: '中',
|
||||||
|
high: '高',
|
||||||
|
any: '任何'
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
modeHome: '卡树数据卡打印机软件',
|
||||||
|
modeDistribute: '数据分发模式',
|
||||||
|
modeCollect: '数据收集模式',
|
||||||
|
ribbon: '色带',
|
||||||
|
ribbonAmount: '余量',
|
||||||
|
status: '状态',
|
||||||
|
serialNo: '序列号'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
version: '版本 v{version}',
|
||||||
|
website: 'www.cardsoon.com',
|
||||||
|
copyright: '版权所有 © 2026 卡树科技'
|
||||||
|
},
|
||||||
|
home: {
|
||||||
|
toolsTitle: '工具',
|
||||||
|
tasksTitle: '任务',
|
||||||
|
dataDistribute: '数据分发',
|
||||||
|
dataDistributeDesc: '分发数据到打印卡片',
|
||||||
|
dataCollect: '数据收集',
|
||||||
|
dataCollectDesc: '从卡片收集导入数据'
|
||||||
|
},
|
||||||
|
distributeConfig: {
|
||||||
|
pathConfig: '路径配置',
|
||||||
|
addNetworkLocation: '添加网络位置',
|
||||||
|
pathHint: '系统将拷贝该目录下的所有子项,但不包含文件夹本身',
|
||||||
|
netCredConfigured: '已配置网络凭据:{hosts}',
|
||||||
|
volumeLabel: '卷标',
|
||||||
|
copyType: '拷贝类型',
|
||||||
|
formatType: '格式化类型',
|
||||||
|
dongle: '加密狗',
|
||||||
|
dongleHint: '(101 为不限次数)',
|
||||||
|
dongleAuthLabel: '授权码',
|
||||||
|
donglePassword: '请输入授权码',
|
||||||
|
templatePreview: '标签预览',
|
||||||
|
addLabel: '添加标签',
|
||||||
|
doubleSide: '双面',
|
||||||
|
singleSideHint: '当前为单面打印机,请选择打印面(正面或背面)',
|
||||||
|
frontSide: 'FRONT',
|
||||||
|
backSide: 'BACK',
|
||||||
|
imageFieldNotSelected: '未选择图片',
|
||||||
|
templateLoaded: '已加载标签模板',
|
||||||
|
templateNoPreview: '模板已打开,但未解析到可预览内容',
|
||||||
|
cleared: '已清空,已恢复初始状态',
|
||||||
|
netCredSaved: '网络凭据已保存',
|
||||||
|
loadProgress: '已加载: {loaded} GB / {total} GB ({percent}%)',
|
||||||
|
calculating: '计算中…',
|
||||||
|
sizeUnknown: '大小未知',
|
||||||
|
pathInvalid: '路径无效',
|
||||||
|
pathReady: '{size} · 待拷贝',
|
||||||
|
pathPattern: '{dir}\\*.*'
|
||||||
|
},
|
||||||
|
copyType: {
|
||||||
|
fileCopy: '文件拷贝',
|
||||||
|
imageBurn: '镜像刻录'
|
||||||
|
},
|
||||||
|
formatType: {
|
||||||
|
none: '不格式化',
|
||||||
|
fat32: 'Fat32',
|
||||||
|
exfat: 'exFat',
|
||||||
|
ntfs: 'NTFS'
|
||||||
|
},
|
||||||
|
dataCollect: {
|
||||||
|
importPath: '数据导入地址',
|
||||||
|
cardOutput: '出卡方向',
|
||||||
|
forwardOutput: '向前出卡',
|
||||||
|
backwardOutput: '向后出卡',
|
||||||
|
noDirSelected: '未选择目录'
|
||||||
|
},
|
||||||
|
running: {
|
||||||
|
taskFailed: '任务失败',
|
||||||
|
taskCompleted: '任务已完成',
|
||||||
|
collectCompleted: '收集已完成',
|
||||||
|
dataCollecting: '数据收集中',
|
||||||
|
taskRunning: '任务执行中',
|
||||||
|
waitingCard: '等待插卡',
|
||||||
|
failedSubCollect: '请检查读卡器与卡片,插入备卡位可自动重试',
|
||||||
|
failedSubDistribute: '请检查设备故障,插入备卡位可自动重试',
|
||||||
|
completedSubCollect: '请点击返回,或插入备卡位继续下一张',
|
||||||
|
completedSubDistribute: '请点击返回,或插入备卡位自动开始下一张',
|
||||||
|
collectingHint: '正在从卡片读取并写入导入目录',
|
||||||
|
waitCardHint: '请插入数据卡,任务将自动执行',
|
||||||
|
runningHint: '任务执行中,请稍候',
|
||||||
|
progressCounter: '任务已经完成{success}次,其中失败次数是{fail}。'
|
||||||
|
},
|
||||||
|
networkDialog: {
|
||||||
|
title: '添加网络位置',
|
||||||
|
hint: '凭据用于访问已添加的映射盘或网络共享路径,请确认主机可访问且账号有效。',
|
||||||
|
host: '主机(IP 或主机名)',
|
||||||
|
share: '共享名(可选)',
|
||||||
|
userName: '用户名',
|
||||||
|
password: '密码',
|
||||||
|
targetUNC: '目标 UNC',
|
||||||
|
hostPlaceholder: '例如 192.168.1.100 或 nas-server',
|
||||||
|
sharePlaceholder: '例如 share,留空表示只挂载到根',
|
||||||
|
userNamePlaceholder: '例如 admin',
|
||||||
|
errHostInvalid: '主机名/IP 不合法',
|
||||||
|
errUserName: '请输入用户名',
|
||||||
|
errPassword: '请输入密码'
|
||||||
|
},
|
||||||
|
workflow: {
|
||||||
|
taskPrep: '任务准备',
|
||||||
|
copyData: '拷贝数据',
|
||||||
|
printCard: '打印卡片',
|
||||||
|
complete: '完成'
|
||||||
|
},
|
||||||
|
notify: {
|
||||||
|
resetSent: '已发送重置指令',
|
||||||
|
resetFailed: '重置失败',
|
||||||
|
rejectUnavailable: '当前环境不支持废卡接口',
|
||||||
|
cardRejected: '已废弃卡片',
|
||||||
|
readCardSent: '已送卡到读卡区',
|
||||||
|
readCardFailed: '读卡失败',
|
||||||
|
ejectCardSent: '已退卡到出卡区',
|
||||||
|
ejectCardFailed: '退卡失败',
|
||||||
|
operationFailed: '操作失败',
|
||||||
|
designStarted: '已启动设计软件',
|
||||||
|
designFailed: '打开设计软件失败,请检查 cardsoon.config.json',
|
||||||
|
designCancelled: '打开设计软件失败,可能已被取消或需要管理员权限',
|
||||||
|
notReady: '系统未就绪,请重启应用或检查打印机与任务目录',
|
||||||
|
notReadyAction: '系统未就绪,无法{action}',
|
||||||
|
stopDistribute: '请先停止数据分发任务',
|
||||||
|
usbCollecting: 'USB 收集进行中,请等待完成',
|
||||||
|
usbCollectInProgress: '数据收集进行中,请先在任务页停止',
|
||||||
|
usbCollectComplete: 'USB 收集完成',
|
||||||
|
pathOpenFailed: '打开目录选择失败',
|
||||||
|
templateOpenFailed: '打开模板选择失败',
|
||||||
|
templateParseFailed: '模板解析失败',
|
||||||
|
dirInvalid: '导入目录无效,无法继续',
|
||||||
|
resubmitCollectFailed: '重新提交收集任务失败',
|
||||||
|
resubmitFailed: '重新提交失败',
|
||||||
|
pollStartFailed: '启动任务轮询失败',
|
||||||
|
pollMonitorFailed: '无法监控卡位,请手动点击返回或重新提交',
|
||||||
|
queryJobFailed: '查询任务失败: {code}',
|
||||||
|
queryUsbFailed: '查询 USB 任务失败: {code}',
|
||||||
|
cancelIssue: '取消任务时出现问题',
|
||||||
|
selectDirFirst: '请先选择数据导入目录',
|
||||||
|
selectDirFirstShort: '请先选择数据导入目录',
|
||||||
|
startUsbFailed: '启动 USB 收集失败',
|
||||||
|
usbWaitComplete: '请先等待 USB 收集完成',
|
||||||
|
cannotEnterRunning: '无法进入运行页,已取消任务',
|
||||||
|
initFailedHint: '初始化失败,请检查任务目录权限'
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
noTask: '请配置拷贝路径或打印模板',
|
||||||
|
invalidTemplate: '请选择 .soon 模板',
|
||||||
|
pathEmpty: '路径不能为空',
|
||||||
|
pathNotExist: '路径不存在: {paths}',
|
||||||
|
noFilesToCopy: '拷贝路径下没有可拷贝的文件',
|
||||||
|
netCredMissing: '请先配置网络位置凭据: {host}',
|
||||||
|
templateNotExist: '模板文件不存在',
|
||||||
|
printFlagMismatch: '打印面数不匹配,请重新选择',
|
||||||
|
singleSideMustPick: '当前为单面打印机,请选择正面或背面',
|
||||||
|
pickSideRequired: '请选择打印面(正面或背面)后再提交',
|
||||||
|
dongleAuthRequired: '请输入授权码',
|
||||||
|
dongleCountInvalid: '加密狗次数须为 0 或 1-101',
|
||||||
|
createJobFailed: '创建任务失败',
|
||||||
|
csvGenFailed: '生成打印变量 CSV 失败'
|
||||||
|
},
|
||||||
|
printerStatus: {
|
||||||
|
idle: '空闲',
|
||||||
|
busy: '忙碌',
|
||||||
|
printing: '正在打印',
|
||||||
|
notConnected: '未连接打印机',
|
||||||
|
notInitialized: '未初始化',
|
||||||
|
initFailed: '初始化失败',
|
||||||
|
ready: '就绪',
|
||||||
|
unknown: '—'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
export default {
|
||||||
|
common: {
|
||||||
|
cancel: '取消',
|
||||||
|
confirm: '確定',
|
||||||
|
close: '關閉',
|
||||||
|
addPath: '新增路徑',
|
||||||
|
clear: '清空',
|
||||||
|
submit: '提交',
|
||||||
|
home: '首頁',
|
||||||
|
stop: '停止',
|
||||||
|
back: '返回',
|
||||||
|
reset: '重設',
|
||||||
|
select: '選擇',
|
||||||
|
resetPrinter: '重設印表機',
|
||||||
|
discardCard: '廢棄卡片',
|
||||||
|
templateDesign: '模板設計',
|
||||||
|
low: '低',
|
||||||
|
mid: '中',
|
||||||
|
high: '高',
|
||||||
|
any: '任何'
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
modeHome: '卡樹數據卡印表機軟體',
|
||||||
|
modeDistribute: '數據分發模式',
|
||||||
|
modeCollect: '數據收集模式',
|
||||||
|
ribbon: '色帶',
|
||||||
|
ribbonAmount: '餘量',
|
||||||
|
status: '狀態',
|
||||||
|
serialNo: '序號'
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
version: '版本 v{version}',
|
||||||
|
website: 'www.cardsoon.com',
|
||||||
|
copyright: '版權所有 © 2026 卡樹科技'
|
||||||
|
},
|
||||||
|
home: {
|
||||||
|
toolsTitle: '工具',
|
||||||
|
tasksTitle: '任務',
|
||||||
|
dataDistribute: '數據分發',
|
||||||
|
dataDistributeDesc: '分發數據到列印卡片',
|
||||||
|
dataCollect: '數據收集',
|
||||||
|
dataCollectDesc: '從卡片收集匯入數據'
|
||||||
|
},
|
||||||
|
distributeConfig: {
|
||||||
|
pathConfig: '路徑配置',
|
||||||
|
addNetworkLocation: '新增網路位置',
|
||||||
|
pathHint: '系統將拷貝該目錄下的所有子項,但不包含資料夾本身',
|
||||||
|
netCredConfigured: '已配置網路憑證:{hosts}',
|
||||||
|
volumeLabel: '卷標',
|
||||||
|
copyType: '拷貝類型',
|
||||||
|
formatType: '格式化類型',
|
||||||
|
dongle: '加密狗',
|
||||||
|
dongleHint: '(101 為不限次數)',
|
||||||
|
dongleAuthLabel: '授權碼',
|
||||||
|
donglePassword: '請輸入授權碼',
|
||||||
|
templatePreview: '標籤預覽',
|
||||||
|
addLabel: '新增標籤',
|
||||||
|
doubleSide: '雙面',
|
||||||
|
singleSideHint: '當前為單面印表機,請選擇列印面(正面或背面)',
|
||||||
|
frontSide: 'FRONT',
|
||||||
|
backSide: 'BACK',
|
||||||
|
imageFieldNotSelected: '未選擇圖片',
|
||||||
|
templateLoaded: '已載入標籤模板',
|
||||||
|
templateNoPreview: '模板已開啟,但未解析到可預覽內容',
|
||||||
|
cleared: '已清空,已恢復初始狀態',
|
||||||
|
netCredSaved: '網路憑證已儲存',
|
||||||
|
loadProgress: '已載入: {loaded} GB / {total} GB ({percent}%)',
|
||||||
|
calculating: '計算中…',
|
||||||
|
sizeUnknown: '大小未知',
|
||||||
|
pathInvalid: '路徑無效',
|
||||||
|
pathReady: '{size} · 待拷貝',
|
||||||
|
pathPattern: '{dir}\\*.*'
|
||||||
|
},
|
||||||
|
copyType: {
|
||||||
|
fileCopy: '檔案拷貝',
|
||||||
|
imageBurn: '鏡像燒錄'
|
||||||
|
},
|
||||||
|
formatType: {
|
||||||
|
none: '不格式化',
|
||||||
|
fat32: 'Fat32',
|
||||||
|
exfat: 'exFat',
|
||||||
|
ntfs: 'NTFS'
|
||||||
|
},
|
||||||
|
dataCollect: {
|
||||||
|
importPath: '數據匯入地址',
|
||||||
|
cardOutput: '出卡方向',
|
||||||
|
forwardOutput: '向前出卡',
|
||||||
|
backwardOutput: '向後出卡',
|
||||||
|
noDirSelected: '未選擇目錄'
|
||||||
|
},
|
||||||
|
running: {
|
||||||
|
taskFailed: '任務失敗',
|
||||||
|
taskCompleted: '任務已完成',
|
||||||
|
collectCompleted: '收集已完成',
|
||||||
|
dataCollecting: '數據收集中',
|
||||||
|
taskRunning: '任務執行中',
|
||||||
|
waitingCard: '等待插卡',
|
||||||
|
failedSubCollect: '請檢查讀卡器與卡片,插入備卡位可自動重試',
|
||||||
|
failedSubDistribute: '請檢查設備故障,插入備卡位可自動重試',
|
||||||
|
completedSubCollect: '請點擊返回,或插入備卡位繼續下一張',
|
||||||
|
completedSubDistribute: '請點擊返回,或插入備卡位自動開始下一張',
|
||||||
|
collectingHint: '正在從卡片讀取並寫入匯入目錄',
|
||||||
|
waitCardHint: '請插入數據卡,任務將自動執行',
|
||||||
|
runningHint: '任務執行中,請稍候',
|
||||||
|
progressCounter: '任務已經完成{success}次,其中失敗次數是{fail}。'
|
||||||
|
},
|
||||||
|
networkDialog: {
|
||||||
|
title: '新增網路位置',
|
||||||
|
hint: '憑證用於存取已新增的映射盤或網路共享路徑,請確認主機可存取且帳號有效。',
|
||||||
|
host: '主機(IP 或主機名)',
|
||||||
|
share: '共享名(可選)',
|
||||||
|
userName: '使用者名稱',
|
||||||
|
password: '密碼',
|
||||||
|
targetUNC: '目標 UNC',
|
||||||
|
hostPlaceholder: '例如 192.168.1.100 或 nas-server',
|
||||||
|
sharePlaceholder: '例如 share,留空表示只掛載到根',
|
||||||
|
userNamePlaceholder: '例如 admin',
|
||||||
|
errHostInvalid: '主機名/IP 不合法',
|
||||||
|
errUserName: '請輸入使用者名稱',
|
||||||
|
errPassword: '請輸入密碼'
|
||||||
|
},
|
||||||
|
workflow: {
|
||||||
|
taskPrep: '任務準備',
|
||||||
|
copyData: '拷貝數據',
|
||||||
|
printCard: '列印卡片',
|
||||||
|
complete: '完成'
|
||||||
|
},
|
||||||
|
notify: {
|
||||||
|
resetSent: '已發送重設指令',
|
||||||
|
resetFailed: '重設失敗',
|
||||||
|
rejectUnavailable: '當前環境不支援廢卡介面',
|
||||||
|
cardRejected: '已廢棄卡片',
|
||||||
|
readCardSent: '已送卡到讀卡區',
|
||||||
|
readCardFailed: '讀卡失敗',
|
||||||
|
ejectCardSent: '已退卡到出卡區',
|
||||||
|
ejectCardFailed: '退卡失敗',
|
||||||
|
operationFailed: '操作失敗',
|
||||||
|
designStarted: '已啟動設計軟體',
|
||||||
|
designFailed: '開啟設計軟體失敗,請檢查 cardsoon.config.json',
|
||||||
|
designCancelled: '開啟設計軟體失敗,可能已被取消或需要管理員權限',
|
||||||
|
notReady: '系統未就緒,請重啟應用或檢查印表機與任務目錄',
|
||||||
|
notReadyAction: '系統未就緒,無法{action}',
|
||||||
|
stopDistribute: '請先停止數據分發任務',
|
||||||
|
usbCollecting: 'USB 收集進行中,請等待完成',
|
||||||
|
usbCollectInProgress: '數據收集進行中,請先在任務頁停止',
|
||||||
|
usbCollectComplete: 'USB 收集完成',
|
||||||
|
pathOpenFailed: '開啟目錄選擇失敗',
|
||||||
|
templateOpenFailed: '開啟模板選擇失敗',
|
||||||
|
templateParseFailed: '模板解析失敗',
|
||||||
|
dirInvalid: '匯入目錄無效,無法繼續',
|
||||||
|
resubmitCollectFailed: '重新提交收集任務失敗',
|
||||||
|
resubmitFailed: '重新提交失敗',
|
||||||
|
pollStartFailed: '啟動任務輪詢失敗',
|
||||||
|
pollMonitorFailed: '無法監控卡位,請手動點擊返回或重新提交',
|
||||||
|
queryJobFailed: '查詢任務失敗: {code}',
|
||||||
|
queryUsbFailed: '查詢 USB 任務失敗: {code}',
|
||||||
|
cancelIssue: '取消任務時出現問題',
|
||||||
|
selectDirFirst: '請先選擇數據匯入目錄',
|
||||||
|
selectDirFirstShort: '請先選擇數據匯入目錄',
|
||||||
|
startUsbFailed: '啟動 USB 收集失敗',
|
||||||
|
usbWaitComplete: '請先等待 USB 收集完成',
|
||||||
|
cannotEnterRunning: '無法進入運行頁,已取消任務',
|
||||||
|
initFailedHint: '初始化失敗,請檢查任務目錄權限'
|
||||||
|
},
|
||||||
|
validation: {
|
||||||
|
noTask: '請配置拷貝路徑或列印模板',
|
||||||
|
invalidTemplate: '請選擇 .soon 模板',
|
||||||
|
pathEmpty: '路徑不能為空',
|
||||||
|
pathNotExist: '路徑不存在: {paths}',
|
||||||
|
noFilesToCopy: '拷貝路徑下沒有可拷貝的檔案',
|
||||||
|
netCredMissing: '請先配置網路位置憑證: {host}',
|
||||||
|
templateNotExist: '模板檔案不存在',
|
||||||
|
printFlagMismatch: '列印面數不匹配,請重新選擇',
|
||||||
|
singleSideMustPick: '當前為單面印表機,請選擇正面或背面',
|
||||||
|
pickSideRequired: '請選擇列印面(正面或背面)後再提交',
|
||||||
|
dongleAuthRequired: '請輸入授權碼',
|
||||||
|
dongleCountInvalid: '加密狗次數須為 0 或 1-101',
|
||||||
|
createJobFailed: '建立任務失敗',
|
||||||
|
csvGenFailed: '生成列印變數 CSV 失敗'
|
||||||
|
},
|
||||||
|
printerStatus: {
|
||||||
|
idle: '空閒',
|
||||||
|
busy: '忙碌',
|
||||||
|
printing: '正在列印',
|
||||||
|
notConnected: '未連接印表機',
|
||||||
|
notInitialized: '未初始化',
|
||||||
|
initFailed: '初始化失敗',
|
||||||
|
ready: '就緒',
|
||||||
|
unknown: '—'
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ import { createApp } from 'vue'
|
|||||||
|
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
import i18n from '@/i18n'
|
||||||
|
|
||||||
import { configGet } from '@/api/cardsoon'
|
import { configGet } from '@/api/cardsoon'
|
||||||
|
|
||||||
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
||||||
@@ -70,6 +72,8 @@ const pinia = createPinia()
|
|||||||
|
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
|
|
||||||
|
app.use(i18n)
|
||||||
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,24 @@
|
|||||||
color: var(--cs-primary);
|
color: var(--cs-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 读卡 / 退卡 并排按钮行 */
|
||||||
|
.m-tool-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-tool-btn--half {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-tool-btn--half i {
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========== 垂直分隔线 ========== */
|
/* ========== 垂直分隔线 ========== */
|
||||||
.m-divider {
|
.m-divider {
|
||||||
width: 1px;
|
width: 1px;
|
||||||
|
|||||||
@@ -117,7 +117,17 @@
|
|||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 授权码标签 */
|
||||||
|
.m-panel-toolbar .c-checkbox-item .dog-auth-label {
|
||||||
|
font-size: 9px;
|
||||||
|
color: #495057;
|
||||||
|
font-weight: 500;
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 提示文字 */
|
/* 提示文字 */
|
||||||
|
|||||||
@@ -3,11 +3,17 @@ export interface PrinterStatusDisplay {
|
|||||||
ribbonAmount: string
|
ribbonAmount: string
|
||||||
statusText: string
|
statusText: string
|
||||||
serialNo: string
|
serialNo: string
|
||||||
|
/** 打印机型号/名称(如 TH80),用于判断单/双面能力 */
|
||||||
|
printerName: string
|
||||||
|
/** 是否为单面打印机(如 TH80),单面打印机不能选"双面"打印 */
|
||||||
|
isSingleSide: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultPrinterStatus: PrinterStatusDisplay = {
|
export const defaultPrinterStatus: PrinterStatusDisplay = {
|
||||||
ribbonType: '—',
|
ribbonType: '—',
|
||||||
ribbonAmount: '—',
|
ribbonAmount: '—',
|
||||||
statusText: '—',
|
statusText: '—',
|
||||||
serialNo: '—'
|
serialNo: '—',
|
||||||
|
printerName: '—',
|
||||||
|
isSingleSide: false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import i18n from '@/i18n'
|
||||||
import { genTaskId } from '@shared/gen-task-id'
|
import { genTaskId } from '@shared/gen-task-id'
|
||||||
import { buildJobConfig } from '@/utils/buildJobConfig'
|
import { buildJobConfig } from '@/utils/buildJobConfig'
|
||||||
import { resolveJobTasks } from '@/utils/validateJobConfig'
|
import { resolveJobTasks } from '@/utils/validateJobConfig'
|
||||||
@@ -6,6 +7,10 @@ import { dllJobCreate, fsWriteJobCsv } from '@/api/cardsoon'
|
|||||||
import { useJobStore } from '@/stores/job'
|
import { useJobStore } from '@/stores/job'
|
||||||
import type { DistributeFormState } from '@/stores/distributeForm'
|
import type { DistributeFormState } from '@/stores/distributeForm'
|
||||||
|
|
||||||
|
function t(key: string, named?: Record<string, unknown>): string {
|
||||||
|
return i18n.global.t(key, named ?? {})
|
||||||
|
}
|
||||||
|
|
||||||
function printFieldRows(form: DistributeFormState) {
|
function printFieldRows(form: DistributeFormState) {
|
||||||
return (form.templatePreview?.fields ?? []).map((f) => ({
|
return (form.templatePreview?.fields ?? []).map((f) => ({
|
||||||
originName: f.originName || f.label.replace(/\[.*\]$/, ''),
|
originName: f.originName || f.label.replace(/\[.*\]$/, ''),
|
||||||
@@ -18,7 +23,7 @@ async function buildJobJson(
|
|||||||
): Promise<{ ok: true; json: string } | { ok: false; message: string }> {
|
): Promise<{ ok: true; json: string } | { ok: false; message: string }> {
|
||||||
const { hasCopy, hasPrint } = resolveJobTasks(form)
|
const { hasCopy, hasPrint } = resolveJobTasks(form)
|
||||||
if (!hasCopy && !hasPrint) {
|
if (!hasCopy && !hasPrint) {
|
||||||
return { ok: false, message: '请配置拷贝路径或打印模板' }
|
return { ok: false, message: t('validation.noTask') }
|
||||||
}
|
}
|
||||||
|
|
||||||
const taskId = genTaskId()
|
const taskId = genTaskId()
|
||||||
@@ -28,7 +33,7 @@ async function buildJobJson(
|
|||||||
if (rows.length > 0) {
|
if (rows.length > 0) {
|
||||||
const csv = await fsWriteJobCsv({ taskId, rows })
|
const csv = await fsWriteJobCsv({ taskId, rows })
|
||||||
if (!csv.ok || !csv.data?.path) {
|
if (!csv.ok || !csv.data?.path) {
|
||||||
return { ok: false, message: csv.message || '生成打印变量 CSV 失败' }
|
return { ok: false, message: csv.message || t('validation.csvGenFailed') }
|
||||||
}
|
}
|
||||||
udfFile = csv.data.path
|
udfFile = csv.data.path
|
||||||
}
|
}
|
||||||
@@ -71,7 +76,7 @@ export async function createDistributeJob(
|
|||||||
|
|
||||||
const created = await dllJobCreate(json, opts)
|
const created = await dllJobCreate(json, opts)
|
||||||
if (!created.ok || !created.data?.jobId) {
|
if (!created.ok || !created.data?.jobId) {
|
||||||
return { ok: false, message: created.message || '创建任务失败' }
|
return { ok: false, message: created.message || t('validation.createJobFailed') }
|
||||||
}
|
}
|
||||||
return { ok: true, jobId: created.data.jobId }
|
return { ok: true, jobId: created.data.jobId }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import type { DistributeFormState } from '@/stores/distributeForm'
|
import type { DistributeFormState } from '@/stores/distributeForm'
|
||||||
|
import i18n from '@/i18n'
|
||||||
|
|
||||||
|
function t(key: string, named?: Record<string, unknown>): string {
|
||||||
|
return i18n.global.t(key, named ?? {})
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveJobTasks(f: DistributeFormState): { hasCopy: boolean; hasPrint: boolean } {
|
export function resolveJobTasks(f: DistributeFormState): { hasCopy: boolean; hasPrint: boolean } {
|
||||||
const hasCopy = f.pathList.some((p) => !!p.path.trim())
|
const hasCopy = f.pathList.some((p) => !!p.path.trim())
|
||||||
@@ -8,8 +13,8 @@ export function resolveJobTasks(f: DistributeFormState): { hasCopy: boolean; has
|
|||||||
|
|
||||||
export function validateJobConfig(f: DistributeFormState): string | null {
|
export function validateJobConfig(f: DistributeFormState): string | null {
|
||||||
const { hasCopy, hasPrint } = resolveJobTasks(f)
|
const { hasCopy, hasPrint } = resolveJobTasks(f)
|
||||||
if (!hasCopy && !hasPrint) return '请配置拷贝路径或打印模板'
|
if (!hasCopy && !hasPrint) return t('validation.noTask')
|
||||||
if (hasPrint && !/\.soon$/i.test(f.templateFile.trim())) return '请选择 .soon 模板'
|
if (hasPrint && !/\.soon$/i.test(f.templateFile.trim())) return t('validation.invalidTemplate')
|
||||||
if (hasCopy && f.pathList.some((p) => !p.path.trim())) return '路径不能为空'
|
if (hasCopy && f.pathList.some((p) => !p.path.trim())) return t('validation.pathEmpty')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import type { DistributeFormState } from '@/stores/distributeForm'
|
import type { DistributeFormState } from '@/stores/distributeForm'
|
||||||
|
import i18n from '@/i18n'
|
||||||
import { fsPathExists } from '@/api/cardsoon'
|
import { fsPathExists } from '@/api/cardsoon'
|
||||||
import { resolveJobTasks, validateJobConfig } from '@/utils/validateJobConfig'
|
import { resolveJobTasks, validateJobConfig } from '@/utils/validateJobConfig'
|
||||||
import { resolveCopyNetworkHosts } from '@/utils/copyNetworkHosts'
|
import { resolveCopyNetworkHosts } from '@/utils/copyNetworkHosts'
|
||||||
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
||||||
|
import { useConfigStore } from '@/stores/config'
|
||||||
|
|
||||||
|
function t(key: string, named?: Record<string, unknown>): string {
|
||||||
|
return i18n.global.t(key, named ?? {})
|
||||||
|
}
|
||||||
|
|
||||||
function totalCopyBytes(form: DistributeFormState): number {
|
function totalCopyBytes(form: DistributeFormState): number {
|
||||||
return form.pathList.reduce((sum, item) => sum + (item.sizeBytes || 0), 0)
|
return form.pathList.reduce((sum, item) => sum + (item.sizeBytes || 0), 0)
|
||||||
@@ -27,10 +33,10 @@ export async function validateJobPreflight(form: DistributeFormState): Promise<s
|
|||||||
if (paths.length > 0) {
|
if (paths.length > 0) {
|
||||||
const ex = await fsPathExists(paths)
|
const ex = await fsPathExists(paths)
|
||||||
if (ex.ok && ex.data?.missing.length) {
|
if (ex.ok && ex.data?.missing.length) {
|
||||||
return `路径不存在: ${ex.data.missing.join(', ')}`
|
return t('validation.pathNotExist', { paths: ex.data.missing.join(', ') })
|
||||||
}
|
}
|
||||||
if (totalCopyBytes(form) <= 0) {
|
if (totalCopyBytes(form) <= 0) {
|
||||||
return '拷贝路径下没有可拷贝的文件'
|
return t('validation.noFilesToCopy')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +46,7 @@ export async function validateJobPreflight(form: DistributeFormState): Promise<s
|
|||||||
for (const host of hosts) {
|
for (const host of hosts) {
|
||||||
const cred = netStore.getCredential(host)
|
const cred = netStore.getCredential(host)
|
||||||
if (!cred?.userName?.trim() || !cred.password) {
|
if (!cred?.userName?.trim() || !cred.password) {
|
||||||
return `请先配置网络位置凭据: ${host}`
|
return t('validation.netCredMissing', { host })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,22 +56,33 @@ export async function validateJobPreflight(form: DistributeFormState): Promise<s
|
|||||||
const soon = form.templateFile.trim()
|
const soon = form.templateFile.trim()
|
||||||
const ex = await fsPathExists([soon])
|
const ex = await fsPathExists([soon])
|
||||||
if (ex.ok && ex.data?.missing.length) {
|
if (ex.ok && ex.data?.missing.length) {
|
||||||
return '模板文件不存在'
|
return t('validation.templateNotExist')
|
||||||
}
|
}
|
||||||
|
|
||||||
const preview = form.templatePreview
|
const preview = form.templatePreview
|
||||||
if (preview && printFlagMismatch(form.printFlag, preview.templateFlag)) {
|
if (preview && printFlagMismatch(form.printFlag, preview.templateFlag)) {
|
||||||
return '打印面数不匹配,请重新选择'
|
return t('validation.printFlagMismatch')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单面打印机:双面模板必须显式选择正面或背面,且不能选双面
|
||||||
|
const configStore = useConfigStore()
|
||||||
|
if (configStore.printer.isSingleSide) {
|
||||||
|
if (preview && preview.templateFlag === 1 && form.printFlag !== 2 && form.printFlag !== 3) {
|
||||||
|
return t('validation.pickSideRequired')
|
||||||
|
}
|
||||||
|
if (form.printFlag === 1) {
|
||||||
|
return t('validation.singleSideMustPick')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form.dongleEnabled) {
|
if (form.dongleEnabled) {
|
||||||
if (!form.dongleAuthCode.trim()) {
|
if (!form.dongleAuthCode.trim()) {
|
||||||
return '请输入授权码'
|
return t('validation.dongleAuthRequired')
|
||||||
}
|
}
|
||||||
const n = form.dongleInstallCount
|
const n = form.dongleInstallCount
|
||||||
if (!Number.isInteger(n) || n < 0 || n > 101) {
|
if (!Number.isInteger(n) || n < 0 || n > 101) {
|
||||||
return '加密狗次数须为 0 或 1-101'
|
return t('validation.dongleCountInvalid')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppShell>
|
<AppShell>
|
||||||
<AppHeader mode="数据收集模式">
|
<AppHeader :mode="t('header.modeCollect')">
|
||||||
<div class="c-nav-group">
|
<div class="c-nav-group">
|
||||||
<NavButton icon="home" label="首页" @click="goHome" />
|
<NavButton icon="home" :label="t('common.home')" @click="goHome" />
|
||||||
<NavButton icon="trash" label="清空" @click="collectStore.reset()" />
|
<NavButton icon="trash" :label="t('common.clear')" @click="collectStore.reset()" />
|
||||||
<NavButton
|
<NavButton
|
||||||
icon="check-circle"
|
icon="check-circle"
|
||||||
label="提交"
|
:label="t('common.submit')"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
:active="true"
|
:active="true"
|
||||||
:disabled="!canSubmit"
|
:disabled="!canSubmit"
|
||||||
@@ -18,34 +18,34 @@
|
|||||||
<section class="m-config-panel">
|
<section class="m-config-panel">
|
||||||
<h3 class="m-panel-title">
|
<h3 class="m-panel-title">
|
||||||
<AppIcon name="folder-open" size="sm" />
|
<AppIcon name="folder-open" size="sm" />
|
||||||
数据导入地址
|
{{ t('dataCollect.importPath') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="m-path-box">
|
<div class="m-path-box">
|
||||||
<span class="m-path-text" :title="collectStore.destPath || undefined">{{
|
<span class="m-path-text" :title="collectStore.destPath || undefined">{{
|
||||||
collectStore.destPath || '未选择目录'
|
collectStore.destPath || t('dataCollect.noDirSelected')
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="m-path-btn" @click="addPath">
|
<button type="button" class="m-path-btn" @click="addPath">
|
||||||
<AppIcon name="plus" size="sm" />
|
<AppIcon name="plus" size="sm" />
|
||||||
添加路径
|
{{ t('common.addPath') }}
|
||||||
</button>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
<div class="m-divider-v" />
|
<div class="m-divider-v" />
|
||||||
<section class="m-config-panel">
|
<section class="m-config-panel">
|
||||||
<h3 class="m-panel-title">
|
<h3 class="m-panel-title">
|
||||||
<AppIcon name="exchange" size="sm" />
|
<AppIcon name="exchange" size="sm" />
|
||||||
出卡方向
|
{{ t('dataCollect.cardOutput') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="m-radio-group">
|
<div class="m-radio-group">
|
||||||
<label class="m-radio-item">
|
<label class="m-radio-item">
|
||||||
<input v-model="collectStore.cardOutput" type="radio" :value="1" />
|
<input v-model="collectStore.cardOutput" type="radio" :value="1" />
|
||||||
<span class="radio-custom" />
|
<span class="radio-custom" />
|
||||||
<span>向前出卡</span>
|
<span>{{ t('dataCollect.forwardOutput') }}</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="m-radio-item">
|
<label class="m-radio-item">
|
||||||
<input v-model="collectStore.cardOutput" type="radio" :value="2" />
|
<input v-model="collectStore.cardOutput" type="radio" :value="2" />
|
||||||
<span class="radio-custom" />
|
<span class="radio-custom" />
|
||||||
<span>向后出卡</span>
|
<span>{{ t('dataCollect.backwardOutput') }}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { notify, notifyRequireInit } from '@/composables/useNotify'
|
import { notify, notifyRequireInit } from '@/composables/useNotify'
|
||||||
import AppShell from '@/layouts/AppShell.vue'
|
import AppShell from '@/layouts/AppShell.vue'
|
||||||
import AppHeader from '@/components/AppHeader.vue'
|
import AppHeader from '@/components/AppHeader.vue'
|
||||||
@@ -68,6 +69,7 @@ import { useAppStore } from '@/stores/app'
|
|||||||
import { dialogOpenDirectory, dllUsbCopy } from '@/api/cardsoon'
|
import { dialogOpenDirectory, dllUsbCopy } from '@/api/cardsoon'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
const collectStore = useCollectStore()
|
const collectStore = useCollectStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ const canSubmit = computed(
|
|||||||
async function addPath(): Promise<void> {
|
async function addPath(): Promise<void> {
|
||||||
const r = await dialogOpenDirectory()
|
const r = await dialogOpenDirectory()
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
notify.error(r.message || '打开目录选择失败')
|
notify.error(r.message || t('notify.pathOpenFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const picked = r.data?.paths[0]
|
const picked = r.data?.paths[0]
|
||||||
@@ -88,21 +90,21 @@ async function addPath(): Promise<void> {
|
|||||||
|
|
||||||
async function onSubmit(): Promise<void> {
|
async function onSubmit(): Promise<void> {
|
||||||
if (!canUse.value) {
|
if (!canUse.value) {
|
||||||
notifyRequireInit('开始 USB 收集')
|
notifyRequireInit(t('notify.startUsbFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (appStore.mode === 'distributing') {
|
if (appStore.mode === 'distributing') {
|
||||||
notify.warning('请先停止数据分发任务')
|
notify.warning(t('notify.stopDistribute'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const dest = collectStore.destPath.trim()
|
const dest = collectStore.destPath.trim()
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
notify.warning('请先选择数据导入目录')
|
notify.warning(t('notify.selectDirFirst'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const r = await dllUsbCopy(dest, collectStore.cardOutput)
|
const r = await dllUsbCopy(dest, collectStore.cardOutput)
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
notify.error(r.message || '启动 USB 收集失败')
|
notify.error(r.message || t('notify.startUsbFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
appStore.setMode('usbCopying')
|
appStore.setMode('usbCopying')
|
||||||
@@ -111,7 +113,7 @@ async function onSubmit(): Promise<void> {
|
|||||||
|
|
||||||
function goHome(): void {
|
function goHome(): void {
|
||||||
if (appStore.mode === 'usbCopying') {
|
if (appStore.mode === 'usbCopying') {
|
||||||
notify.warning('数据收集进行中,请先在任务页停止')
|
notify.warning(t('notify.usbCollectInProgress'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
router.push('/home')
|
router.push('/home')
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppShell>
|
<AppShell>
|
||||||
<AppHeader mode="数据分发模式">
|
<AppHeader :mode="t('header.modeDistribute')">
|
||||||
<div class="c-nav-group">
|
<div class="c-nav-group">
|
||||||
<NavButton icon="home" label="首页" @click="router.push('/home')" />
|
<NavButton icon="home" :label="t('common.home')" @click="router.push('/home')" />
|
||||||
<NavButton icon="trash" label="清空" @click="onClear" />
|
<NavButton icon="trash" :label="t('common.clear')" @click="onClear" />
|
||||||
<NavButton
|
<NavButton
|
||||||
icon="check-circle"
|
icon="check-circle"
|
||||||
label="提交"
|
:label="t('common.submit')"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
:active="true"
|
:active="true"
|
||||||
:disabled="!canSubmit"
|
:disabled="!canSubmit"
|
||||||
@@ -17,38 +17,38 @@
|
|||||||
<main class="app-shell__main l-main-flex">
|
<main class="app-shell__main l-main-flex">
|
||||||
<section class="c-panel m-panel--left">
|
<section class="c-panel m-panel--left">
|
||||||
<div class="c-panel__header">
|
<div class="c-panel__header">
|
||||||
<span class="c-panel__title">路径配置</span>
|
<span class="c-panel__title">{{ t('distributeConfig.pathConfig') }}</span>
|
||||||
<div class="c-nav-group">
|
<div class="c-nav-group">
|
||||||
<button type="button" class="c-button-cs" @click="addPath">
|
<button type="button" class="c-button-cs" @click="addPath">
|
||||||
添加路径
|
{{ t('common.addPath') }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="c-button-cs" @click="openNetworkDialog">
|
<button type="button" class="c-button-cs" @click="openNetworkDialog">
|
||||||
添加网络位置
|
{{ t('distributeConfig.addNetworkLocation') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="m-path-hint">
|
<div class="m-path-hint">
|
||||||
<AppIcon name="info-circle" size="sm" />
|
<AppIcon name="info-circle" size="sm" />
|
||||||
<span>系统将拷贝该目录下的所有子项,但不包含文件夹本身</span>
|
<span>{{ t('distributeConfig.pathHint') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="configuredHosts.length" class="m-net-cred-hint">
|
<div v-if="configuredHosts.length" class="m-net-cred-hint">
|
||||||
已配置网络凭据:{{ configuredHosts.join('、') }}
|
{{ t('distributeConfig.netCredConfigured', { hosts: configuredHosts.join('、') }) }}
|
||||||
</div>
|
</div>
|
||||||
<div class="m-panel-toolbar">
|
<div class="m-panel-toolbar">
|
||||||
<div class="toolbar-row">
|
<div class="toolbar-row">
|
||||||
<div class="toolbar-item">
|
<div class="toolbar-item">
|
||||||
<span>卷标</span>
|
<span>{{ t('distributeConfig.volumeLabel') }}</span>
|
||||||
<input v-model="formStore.volumeLabel" type="text" class="c-input" />
|
<input v-model="formStore.volumeLabel" type="text" class="c-input" />
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-item">
|
<div class="toolbar-item">
|
||||||
<span>拷贝类型</span>
|
<span>{{ t('distributeConfig.copyType') }}</span>
|
||||||
<AppSelect v-model="formStore.copyType" :items="COPY_TYPE_OPTIONS" />
|
<AppSelect v-model="formStore.copyType" :items="copyTypeItems" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-row">
|
<div class="toolbar-row">
|
||||||
<div class="toolbar-item">
|
<div class="toolbar-item">
|
||||||
<span>格式化类型</span>
|
<span>{{ t('distributeConfig.formatType') }}</span>
|
||||||
<AppSelect v-model="formStore.formatType" :items="FORMAT_TYPE_OPTIONS" />
|
<AppSelect v-model="formStore.formatType" :items="formatTypeItems" />
|
||||||
</div>
|
</div>
|
||||||
<label class="c-checkbox-item">
|
<label class="c-checkbox-item">
|
||||||
<input
|
<input
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
@change="onDongleToggle"
|
@change="onDongleToggle"
|
||||||
/>
|
/>
|
||||||
<span>加密狗</span>
|
<span>{{ t('distributeConfig.dongle') }}</span>
|
||||||
<input
|
<input
|
||||||
:value="dongleInputValue"
|
:value="dongleInputValue"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -68,14 +68,16 @@
|
|||||||
@input="onDongleCountInput"
|
@input="onDongleCountInput"
|
||||||
/>
|
/>
|
||||||
<span class="dog-hint">{{ dongleHint }}</span>
|
<span class="dog-hint">{{ dongleHint }}</span>
|
||||||
|
<template v-if="formStore.dongleEnabled">
|
||||||
|
<span class="dog-auth-label">{{ t('distributeConfig.dongleAuthLabel') }}</span>
|
||||||
<input
|
<input
|
||||||
v-if="formStore.dongleEnabled"
|
|
||||||
v-model="formStore.dongleAuthCode"
|
v-model="formStore.dongleAuthCode"
|
||||||
type="password"
|
type="password"
|
||||||
class="c-input dog-auth"
|
class="c-input dog-auth"
|
||||||
placeholder="授权码"
|
:placeholder="t('distributeConfig.donglePassword')"
|
||||||
@input="onDongleAuthInput"
|
@input="onDongleAuthInput"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,18 +101,33 @@
|
|||||||
</section>
|
</section>
|
||||||
<section class="c-panel m-panel--right">
|
<section class="c-panel m-panel--right">
|
||||||
<div class="c-panel__header">
|
<div class="c-panel__header">
|
||||||
<span class="c-panel__title">标签预览</span>
|
<span class="c-panel__title">{{ t('distributeConfig.templatePreview') }}</span>
|
||||||
<div class="c-nav-group">
|
<div class="c-nav-group">
|
||||||
<button type="button" class="c-button-cs" @click="pickTemplate">
|
<button type="button" class="c-button-cs" @click="pickTemplate">
|
||||||
添加标签
|
{{ t('distributeConfig.addLabel') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasTemplatePreview && hasDoubleSide" class="m-print-side-picker">
|
<div v-if="templatePreviewLoaded && hasDoubleSide" class="m-print-side-picker">
|
||||||
|
<template v-if="isSingleSidePrinter">
|
||||||
|
<label class="m-print-side-option">
|
||||||
|
<input v-model="formStore.printFlag" type="radio" :value="2" />
|
||||||
|
<span>{{ t('distributeConfig.frontSide') }}</span>
|
||||||
|
</label>
|
||||||
|
<label class="m-print-side-option">
|
||||||
|
<input v-model="formStore.printFlag" type="radio" :value="3" />
|
||||||
|
<span>{{ t('distributeConfig.backSide') }}</span>
|
||||||
|
</label>
|
||||||
|
<span v-if="!sidePicked" class="m-print-side-hint">
|
||||||
|
{{ t('distributeConfig.singleSideHint') }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<label class="m-print-side-option">
|
<label class="m-print-side-option">
|
||||||
<input v-model="formStore.printFlag" type="radio" :value="1" />
|
<input v-model="formStore.printFlag" type="radio" :value="1" />
|
||||||
<span>双面</span>
|
<span>{{ t('distributeConfig.doubleSide') }}</span>
|
||||||
</label>
|
</label>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="c-preview-area">
|
<div class="c-preview-area">
|
||||||
<div
|
<div
|
||||||
@@ -155,7 +172,7 @@
|
|||||||
<span class="c-path-cell__text" :title="row.value">{{
|
<span class="c-path-cell__text" :title="row.value">{{
|
||||||
imageFieldLabel(row.value)
|
imageFieldLabel(row.value)
|
||||||
}}</span>
|
}}</span>
|
||||||
<button type="button" class="c-field-pick" @click="pickFieldImage(idx)">选择</button>
|
<button type="button" class="c-field-pick" @click="pickFieldImage(idx)">{{ t('common.select') }}</button>
|
||||||
</td>
|
</td>
|
||||||
<td v-else-if="isTextField(row)">
|
<td v-else-if="isTextField(row)">
|
||||||
<input
|
<input
|
||||||
@@ -186,6 +203,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { cleanPathPattern } from '@shared/path-pattern'
|
import { cleanPathPattern } from '@shared/path-pattern'
|
||||||
import { extractDriveLetter } from '@shared/network-host'
|
import { extractDriveLetter } from '@shared/network-host'
|
||||||
@@ -197,11 +215,12 @@ import NavButton from '@/components/NavButton.vue'
|
|||||||
import AppIcon from '@/components/AppIcon.vue'
|
import AppIcon from '@/components/AppIcon.vue'
|
||||||
import AppSelect from '@/components/AppSelect.vue'
|
import AppSelect from '@/components/AppSelect.vue'
|
||||||
import NetworkPathDialog from '@/components/NetworkPathDialog.vue'
|
import NetworkPathDialog from '@/components/NetworkPathDialog.vue'
|
||||||
import { COPY_TYPE_OPTIONS, FORMAT_TYPE_OPTIONS } from '@/constants/selectOptions'
|
import { copyTypeOptions, formatTypeOptions } from '@/constants/selectOptions'
|
||||||
import { CARD_CAPACITY_BYTES, CARD_CAPACITY_GB } from '@/constants/cardCapacity'
|
import { CARD_CAPACITY_BYTES, CARD_CAPACITY_GB } from '@/constants/cardCapacity'
|
||||||
import { useDistributeFormStore } from '@/stores/distributeForm'
|
import { useDistributeFormStore } from '@/stores/distributeForm'
|
||||||
import { useJobStore } from '@/stores/job'
|
import { useJobStore } from '@/stores/job'
|
||||||
import { useAppStore } from '@/stores/app'
|
import { useAppStore } from '@/stores/app'
|
||||||
|
import { useConfigStore } from '@/stores/config'
|
||||||
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
import { useNetworkAuthStore } from '@/stores/networkAuth'
|
||||||
import { useDongleAuthStore } from '@/stores/dongleAuth'
|
import { useDongleAuthStore } from '@/stores/dongleAuth'
|
||||||
import { validateJobPreflight } from '@/utils/validateJobPreflight'
|
import { validateJobPreflight } from '@/utils/validateJobPreflight'
|
||||||
@@ -219,9 +238,11 @@ import {
|
|||||||
} from '@/api/cardsoon'
|
} from '@/api/cardsoon'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
const formStore = useDistributeFormStore()
|
const formStore = useDistributeFormStore()
|
||||||
const jobStore = useJobStore()
|
const jobStore = useJobStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
const configStore = useConfigStore()
|
||||||
const netStore = useNetworkAuthStore()
|
const netStore = useNetworkAuthStore()
|
||||||
const dongleStore = useDongleAuthStore()
|
const dongleStore = useDongleAuthStore()
|
||||||
|
|
||||||
@@ -256,13 +277,26 @@ const templateFlag = computed(() => formStore.templatePreview?.templateFlag ?? 0
|
|||||||
|
|
||||||
const hasDoubleSide = computed(() => templateFlag.value === 1)
|
const hasDoubleSide = computed(() => templateFlag.value === 1)
|
||||||
|
|
||||||
|
const isSingleSidePrinter = computed(() => configStore.printer.isSingleSide)
|
||||||
|
|
||||||
|
/** 模板已加载(无论有无可编辑字段),用于选面区显示 */
|
||||||
|
const templatePreviewLoaded = computed(() => !!formStore.templatePreview)
|
||||||
|
|
||||||
|
/** 单面打印机 + 双面模板时,必须显式选择正面或背面 */
|
||||||
|
const sidePicked = computed(
|
||||||
|
() => formStore.printFlag === 2 || formStore.printFlag === 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const copyTypeItems = computed(() => copyTypeOptions(t))
|
||||||
|
const formatTypeItems = computed(() => formatTypeOptions(t))
|
||||||
|
|
||||||
const canPickFront = computed(() => templateFlag.value !== 3)
|
const canPickFront = computed(() => templateFlag.value !== 3)
|
||||||
|
|
||||||
const canPickBack = computed(() => templateFlag.value !== 2)
|
const canPickBack = computed(() => templateFlag.value !== 2)
|
||||||
|
|
||||||
const loadProgressText = computed(() => {
|
const loadProgressText = computed(() => {
|
||||||
const loadedGb = formatBytesAsGb(totalLoadedBytes.value)
|
const loadedGb = formatBytesAsGb(totalLoadedBytes.value)
|
||||||
return `已加载: ${loadedGb} GB / ${CARD_CAPACITY_GB} GB (${loadPercent.value}%)`
|
return t('distributeConfig.loadProgress', { loaded: loadedGb, total: CARD_CAPACITY_GB, percent: loadPercent.value })
|
||||||
})
|
})
|
||||||
|
|
||||||
function isTextField(row: TemplateFieldRow): boolean {
|
function isTextField(row: TemplateFieldRow): boolean {
|
||||||
@@ -280,7 +314,9 @@ function imageFieldLabel(value: string): string {
|
|||||||
return parts[parts.length - 1] || v
|
return parts[parts.length - 1] || v
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultPrintFlagForTemplate(flag: number): number {
|
function defaultPrintFlagForTemplate(flag: number, isSingleSide: boolean): number {
|
||||||
|
// 单面打印机 + 双面模板:不默认,强制用户选择打印面(0=未选择)
|
||||||
|
if (isSingleSide && flag === 1) return 0
|
||||||
if (flag === 1 || flag === 2 || flag === 3) return flag
|
if (flag === 1 || flag === 2 || flag === 3) return flag
|
||||||
return 2
|
return 2
|
||||||
}
|
}
|
||||||
@@ -308,7 +344,7 @@ async function pickFieldImage(idx: number): Promise<void> {
|
|||||||
|
|
||||||
function onClear(): void {
|
function onClear(): void {
|
||||||
formStore.reset()
|
formStore.reset()
|
||||||
notify.info('已清空,已恢复初始状态')
|
notify.info(t('distributeConfig.cleared'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const dongleInputValue = computed(() =>
|
const dongleInputValue = computed(() =>
|
||||||
@@ -346,7 +382,7 @@ function onDongleAuthInput(): void {
|
|||||||
async function addPath(): Promise<void> {
|
async function addPath(): Promise<void> {
|
||||||
const r = await dialogOpenDirectory()
|
const r = await dialogOpenDirectory()
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
notify.error(r.message || '打开目录选择失败')
|
notify.error(r.message || t('notify.pathOpenFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!r.data?.paths.length) return
|
if (!r.data?.paths.length) return
|
||||||
@@ -354,7 +390,7 @@ async function addPath(): Promise<void> {
|
|||||||
const idx = formStore.pathList.length
|
const idx = formStore.pathList.length
|
||||||
formStore.pathList.push({
|
formStore.pathList.push({
|
||||||
path: `${dir}\\*.*`,
|
path: `${dir}\\*.*`,
|
||||||
meta: '计算中…',
|
meta: t('distributeConfig.calculating'),
|
||||||
sizeBytes: 0
|
sizeBytes: 0
|
||||||
})
|
})
|
||||||
await refreshPathSize(idx, dir)
|
await refreshPathSize(idx, dir)
|
||||||
@@ -428,13 +464,16 @@ async function pickTemplate(): Promise<void> {
|
|||||||
}
|
}
|
||||||
formStore.templateFile = soonPath
|
formStore.templateFile = soonPath
|
||||||
formStore.templatePreview = parsed.data
|
formStore.templatePreview = parsed.data
|
||||||
formStore.printFlag = defaultPrintFlagForTemplate(parsed.data.templateFlag)
|
formStore.printFlag = defaultPrintFlagForTemplate(
|
||||||
|
parsed.data.templateFlag,
|
||||||
|
isSingleSidePrinter.value
|
||||||
|
)
|
||||||
const { fields, frontImageUrl, backImageUrl } = parsed.data
|
const { fields, frontImageUrl, backImageUrl } = parsed.data
|
||||||
if (!fields.length && !frontImageUrl && !backImageUrl) {
|
if (!fields.length && !frontImageUrl && !backImageUrl) {
|
||||||
notify.warning('模板已打开,但未解析到可预览内容')
|
notify.warning(t('distributeConfig.templateNoPreview'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
notify.success('已加载标签模板')
|
notify.success(t('distributeConfig.templateLoaded'))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSubmit(): Promise<void> {
|
async function onSubmit(): Promise<void> {
|
||||||
@@ -468,7 +507,7 @@ async function onSubmit(): Promise<void> {
|
|||||||
await dllJobCancel(newJobId)
|
await dllJobCancel(newJobId)
|
||||||
jobStore.clearActiveJob()
|
jobStore.clearActiveJob()
|
||||||
appStore.setMode('ready')
|
appStore.setMode('ready')
|
||||||
notify.error('无法进入运行页,已取消任务')
|
notify.error(t('notify.cannotEnterRunning'))
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
jobStore.submitting = false
|
jobStore.submitting = false
|
||||||
@@ -500,6 +539,11 @@ async function onSubmit(): Promise<void> {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-print-side-hint {
|
||||||
|
color: #e6a23c;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.c-card-small--slot {
|
.c-card-small--slot {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -2,29 +2,26 @@
|
|||||||
<AppShell>
|
<AppShell>
|
||||||
<AppHeader :mode="headerMode">
|
<AppHeader :mode="headerMode">
|
||||||
<div v-if="phase === 'failed'" class="c-nav-group">
|
<div v-if="phase === 'failed'" class="c-nav-group">
|
||||||
<NavButton icon="arrow-left" label="返回" @click="onFailedBack" />
|
<NavButton icon="arrow-left" :label="t('common.back')" @click="onFailedBack" />
|
||||||
<NavButton icon="redo" label="重置" variant="primary" @click="onFailedReset" />
|
<NavButton icon="redo" :label="t('common.reset')" variant="primary" @click="onFailedReset" />
|
||||||
</div>
|
</div>
|
||||||
<NavButton
|
<NavButton
|
||||||
v-else-if="phase === 'completed'"
|
v-else-if="phase === 'completed'"
|
||||||
icon="home"
|
icon="home"
|
||||||
label="返回"
|
:label="t('common.back')"
|
||||||
@click="onReturn"
|
@click="onReturn"
|
||||||
/>
|
/>
|
||||||
<NavButton v-else icon="stop" label="停止" variant="stop" @click="onStop" />
|
<NavButton v-else icon="stop" :label="t('common.stop')" variant="stop" @click="onStop" />
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
<main class="app-shell__main l-main-full">
|
<main class="app-shell__main l-main-full">
|
||||||
<section class="l-hero-container">
|
<section class="l-hero-container">
|
||||||
<div class="m-left-panel">
|
<div class="m-left-panel">
|
||||||
<div class="c-status-panel">
|
<div class="c-status-panel">
|
||||||
<template v-if="phase === 'failed'">
|
<template v-if="phase === 'failed'">
|
||||||
<h2 class="c-status-title is-error">任务失败</h2>
|
<h2 class="c-status-title is-error">{{ t('running.taskFailed') }}</h2>
|
||||||
<p class="c-status-sub">{{ failedSub }}</p>
|
<p class="c-status-sub">{{ failedSub }}</p>
|
||||||
<p class="c-status-counter">
|
<p class="c-status-counter">
|
||||||
任务已经完成<span class="ok">{{ successCount }}</span>次,其中失败次数是<span
|
{{ t('running.progressCounter', { success: successCount, fail: failCount }) }}
|
||||||
class="err"
|
|
||||||
>{{ failCount }}</span
|
|
||||||
>。
|
|
||||||
</p>
|
</p>
|
||||||
<p v-if="failureErrorText" class="m-error-detail">{{ failureErrorText }}</p>
|
<p v-if="failureErrorText" class="m-error-detail">{{ failureErrorText }}</p>
|
||||||
</template>
|
</template>
|
||||||
@@ -34,10 +31,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<p class="c-status-sub">{{ statusSub }}</p>
|
<p class="c-status-sub">{{ statusSub }}</p>
|
||||||
<p class="c-status-counter">
|
<p class="c-status-counter">
|
||||||
任务已经完成<span class="ok">{{ successCount }}</span>次,其中失败次数是<span
|
{{ t('running.progressCounter', { success: successCount, fail: failCount }) }}
|
||||||
class="err"
|
|
||||||
>{{ failCount }}</span
|
|
||||||
>。
|
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,6 +75,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { notify } from '@/composables/useNotify'
|
import { notify } from '@/composables/useNotify'
|
||||||
import { refreshLiveStatus } from '@/composables/usePrinterStatus'
|
import { refreshLiveStatus } from '@/composables/usePrinterStatus'
|
||||||
import AppShell from '@/layouts/AppShell.vue'
|
import AppShell from '@/layouts/AppShell.vue'
|
||||||
@@ -122,13 +117,14 @@ const CIRCLE_LEN = 283
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const { t } = useI18n()
|
||||||
const jobStore = useJobStore()
|
const jobStore = useJobStore()
|
||||||
const collectStore = useCollectStore()
|
const collectStore = useCollectStore()
|
||||||
const formStore = useDistributeFormStore()
|
const formStore = useDistributeFormStore()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
|
|
||||||
const isCollect = computed(() => route.name === 'collect-running')
|
const isCollect = computed(() => route.name === 'collect-running')
|
||||||
const headerMode = computed(() => (isCollect.value ? '数据收集模式' : '数据分发模式'))
|
const headerMode = computed(() => (isCollect.value ? t('header.modeCollect') : t('header.modeDistribute')))
|
||||||
const successCount = computed(() =>
|
const successCount = computed(() =>
|
||||||
isCollect.value ? collectStore.successCount : jobStore.successCount
|
isCollect.value ? collectStore.successCount : jobStore.successCount
|
||||||
)
|
)
|
||||||
@@ -156,30 +152,30 @@ const displayProgress = computed(() => Math.round(progress.value))
|
|||||||
|
|
||||||
const failedSub = computed(() =>
|
const failedSub = computed(() =>
|
||||||
isCollect.value
|
isCollect.value
|
||||||
? '请检查读卡器与卡片,插入备卡位可自动重试'
|
? t('running.failedSubCollect')
|
||||||
: '请检查设备故障,插入备卡位可自动重试'
|
: t('running.failedSubDistribute')
|
||||||
)
|
)
|
||||||
|
|
||||||
const statusTitle = computed(() => {
|
const statusTitle = computed(() => {
|
||||||
if (phase.value === 'completed') {
|
if (phase.value === 'completed') {
|
||||||
return isCollect.value ? '收集已完成' : '任务已完成'
|
return isCollect.value ? t('running.collectCompleted') : t('running.taskCompleted')
|
||||||
}
|
}
|
||||||
if (isCollect.value) {
|
if (isCollect.value) {
|
||||||
return collectHint.value || '数据收集中'
|
return collectHint.value || t('running.dataCollecting')
|
||||||
}
|
}
|
||||||
return waitCard.value ? '等待插卡' : '任务执行中'
|
return waitCard.value ? t('running.waitingCard') : t('running.taskRunning')
|
||||||
})
|
})
|
||||||
|
|
||||||
const statusSub = computed(() => {
|
const statusSub = computed(() => {
|
||||||
if (phase.value === 'completed') {
|
if (phase.value === 'completed') {
|
||||||
return isCollect.value
|
return isCollect.value
|
||||||
? '请点击返回,或插入备卡位继续下一张'
|
? t('running.completedSubCollect')
|
||||||
: '请点击返回,或插入备卡位自动开始下一张'
|
: t('running.completedSubDistribute')
|
||||||
}
|
}
|
||||||
if (isCollect.value) {
|
if (isCollect.value) {
|
||||||
return collectHint.value || '正在从卡片读取并写入导入目录'
|
return collectHint.value || t('running.collectingHint')
|
||||||
}
|
}
|
||||||
return waitCard.value ? '请插入数据卡,任务将自动执行' : '任务执行中,请稍候'
|
return waitCard.value ? t('running.waitCardHint') : t('running.runningHint')
|
||||||
})
|
})
|
||||||
|
|
||||||
function setProgress(value: number): void {
|
function setProgress(value: number): void {
|
||||||
@@ -229,7 +225,7 @@ async function startCardPositionWatch(
|
|||||||
)
|
)
|
||||||
const cardStarted = await pollCardPositionStart(sessionMode)
|
const cardStarted = await pollCardPositionStart(sessionMode)
|
||||||
if (!cardStarted.ok) {
|
if (!cardStarted.ok) {
|
||||||
notify.warning(cardStarted.message || '无法监控卡位,请手动点击返回或重新提交')
|
notify.warning(cardStarted.message || t('notify.pollMonitorFailed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +302,7 @@ async function resubmitTask(): Promise<void> {
|
|||||||
if (isCollect.value) {
|
if (isCollect.value) {
|
||||||
const dest = collectStore.destPath.trim()
|
const dest = collectStore.destPath.trim()
|
||||||
if (!dest) {
|
if (!dest) {
|
||||||
await backToWait('导入目录无效,无法继续')
|
await backToWait(t('notify.dirInvalid'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
phase.value = 'running'
|
phase.value = 'running'
|
||||||
@@ -317,7 +313,7 @@ async function resubmitTask(): Promise<void> {
|
|||||||
usbQueryFailStreak = 0
|
usbQueryFailStreak = 0
|
||||||
const r = await dllUsbCopy(dest, collectStore.cardOutput, { resubmit: true })
|
const r = await dllUsbCopy(dest, collectStore.cardOutput, { resubmit: true })
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
await backToWait(r.message || '重新提交收集任务失败')
|
await backToWait(r.message || t('notify.resubmitCollectFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
usbAwaitNewCycle = true
|
usbAwaitNewCycle = true
|
||||||
@@ -348,7 +344,7 @@ async function resubmitTask(): Promise<void> {
|
|||||||
jobStore.setActiveJob(created.jobId)
|
jobStore.setActiveJob(created.jobId)
|
||||||
const started = await pollJobStart(created.jobId)
|
const started = await pollJobStart(created.jobId)
|
||||||
if (!started.ok) {
|
if (!started.ok) {
|
||||||
await backToWait(started.message || '启动任务轮询失败')
|
await backToWait(started.message || t('notify.pollStartFailed'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
unsubJob = onJobPollTick((payload) => applyJobProgress(payload as JobPollPayload))
|
unsubJob = onJobPollTick((payload) => applyJobProgress(payload as JobPollPayload))
|
||||||
@@ -366,7 +362,7 @@ function applyJobProgress(p: JobPollPayload): void {
|
|||||||
queryFailStreak += 1
|
queryFailStreak += 1
|
||||||
if (queryFailStreak < 3) return
|
if (queryFailStreak < 3) return
|
||||||
jobStore.failCount += 1
|
jobStore.failCount += 1
|
||||||
void enterFailedPhase(`查询任务失败: ${p.queryErrorCode}`)
|
void enterFailedPhase(t('notify.queryJobFailed', { code: p.queryErrorCode }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
queryFailStreak = 0
|
queryFailStreak = 0
|
||||||
@@ -397,7 +393,7 @@ function applyUsbProgress(p: UsbPollPayload): void {
|
|||||||
usbQueryFailStreak += 1
|
usbQueryFailStreak += 1
|
||||||
if (usbQueryFailStreak < 3) return
|
if (usbQueryFailStreak < 3) return
|
||||||
collectStore.failCount += 1
|
collectStore.failCount += 1
|
||||||
void enterFailedPhase(`查询 USB 任务失败: ${p.queryCode}`)
|
void enterFailedPhase(t('notify.queryUsbFailed', { code: p.queryCode }))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
usbQueryFailStreak = 0
|
usbQueryFailStreak = 0
|
||||||
@@ -415,7 +411,7 @@ function applyUsbProgress(p: UsbPollPayload): void {
|
|||||||
collectStore.successCount += 1
|
collectStore.successCount += 1
|
||||||
workflowStep.value = 3
|
workflowStep.value = 3
|
||||||
collectHint.value = usbTaskStatusHint(p.taskStatus)
|
collectHint.value = usbTaskStatusHint(p.taskStatus)
|
||||||
notify.success('USB 收集完成')
|
notify.success(t('notify.usbCollectComplete'))
|
||||||
void enterCompletedPhase()
|
void enterCompletedPhase()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -438,7 +434,7 @@ async function finishDistribute(
|
|||||||
if (id && !skipCancel) {
|
if (id && !skipCancel) {
|
||||||
const r = await dllJobCancel(id)
|
const r = await dllJobCancel(id)
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
notify.warning(r.message || '取消任务时出现问题')
|
notify.warning(r.message || t('notify.cancelIssue'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
jobStore.clearActiveJob()
|
jobStore.clearActiveJob()
|
||||||
@@ -476,7 +472,7 @@ onMounted(async () => {
|
|||||||
setProgress(0)
|
setProgress(0)
|
||||||
const started = await pollJobStart(jobStore.jobId)
|
const started = await pollJobStart(jobStore.jobId)
|
||||||
if (!started.ok) {
|
if (!started.ok) {
|
||||||
notify.error(started.message || '启动任务轮询失败')
|
notify.error(started.message || t('notify.pollStartFailed'))
|
||||||
await finishDistribute(false)
|
await finishDistribute(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<AppShell>
|
<AppShell>
|
||||||
<AppHeader mode="卡树数据卡打印机软件" />
|
<AppHeader :mode="t('header.modeHome')" />
|
||||||
<main class="app-shell__main l-dashboard">
|
<main class="app-shell__main l-dashboard">
|
||||||
<section class="m-tool-section">
|
<section class="m-tool-section">
|
||||||
<h3 class="m-section-title">工具</h3>
|
<h3 class="m-section-title">{{ t('home.toolsTitle') }}</h3>
|
||||||
<div class="m-tool-grid">
|
<div class="m-tool-grid">
|
||||||
<button type="button" class="m-tool-btn" @click="onReset">
|
<button type="button" class="m-tool-btn" @click="onReset">
|
||||||
<AppIcon name="redo" />
|
<AppIcon name="redo" />
|
||||||
<span>重置打印机</span>
|
<span>{{ t('common.resetPrinter') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="m-tool-btn" @click="onReject">
|
<button type="button" class="m-tool-btn" @click="onReject">
|
||||||
<AppIcon name="trash" />
|
<AppIcon name="trash" />
|
||||||
<span>废弃卡片</span>
|
<span>{{ t('common.discardCard') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
<div class="m-tool-row">
|
||||||
|
<button type="button" class="m-tool-btn m-tool-btn--half" @click="onReadCard">
|
||||||
|
<AppIcon name="id-card" />
|
||||||
|
<span>{{ t('home.readCard') }}</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="m-tool-btn m-tool-btn--half" @click="onEjectCard">
|
||||||
|
<AppIcon name="eject" />
|
||||||
|
<span>{{ t('home.ejectCard') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button type="button" class="m-tool-btn" @click="onTemplate">
|
<button type="button" class="m-tool-btn" @click="onTemplate">
|
||||||
<AppIcon name="paint-brush" />
|
<AppIcon name="paint-brush" />
|
||||||
<span>模板设计</span>
|
<span>{{ t('common.templateDesign') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div class="m-divider" />
|
<div class="m-divider" />
|
||||||
<section class="m-task-section">
|
<section class="m-task-section">
|
||||||
<h3 class="m-section-title">任务</h3>
|
<h3 class="m-section-title">{{ t('home.tasksTitle') }}</h3>
|
||||||
<div class="m-task-grid">
|
<div class="m-task-grid">
|
||||||
<button type="button" class="m-task-card" @click="goDistribute">
|
<button type="button" class="m-task-card" @click="goDistribute">
|
||||||
<div class="m-task-icon">
|
<div class="m-task-icon">
|
||||||
<AppIcon name="share" />
|
<AppIcon name="share" />
|
||||||
</div>
|
</div>
|
||||||
<div class="m-task-info">
|
<div class="m-task-info">
|
||||||
<h4>数据分发</h4>
|
<h4>{{ t('home.dataDistribute') }}</h4>
|
||||||
<p>分发数据到打印卡片</p>
|
<p>{{ t('home.dataDistributeDesc') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="m-task-card" @click="goCollect">
|
<button type="button" class="m-task-card" @click="goCollect">
|
||||||
@@ -37,8 +47,8 @@
|
|||||||
<AppIcon name="download" />
|
<AppIcon name="download" />
|
||||||
</div>
|
</div>
|
||||||
<div class="m-task-info">
|
<div class="m-task-info">
|
||||||
<h4>数据收集</h4>
|
<h4>{{ t('home.dataCollect') }}</h4>
|
||||||
<p>从卡片收集导入数据</p>
|
<p>{{ t('home.dataCollectDesc') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,6 +60,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { notify, notifyRequireInit } from '@/composables/useNotify'
|
import { notify, notifyRequireInit } from '@/composables/useNotify'
|
||||||
import { refreshLiveStatus } from '@/composables/usePrinterStatus'
|
import { refreshLiveStatus } from '@/composables/usePrinterStatus'
|
||||||
@@ -59,8 +70,9 @@ import AppFooter from '@/components/AppFooter.vue'
|
|||||||
import AppIcon from '@/components/AppIcon.vue'
|
import AppIcon from '@/components/AppIcon.vue'
|
||||||
import { useAppStore } from '@/stores/app'
|
import { useAppStore } from '@/stores/app'
|
||||||
import { useConfigStore } from '@/stores/config'
|
import { useConfigStore } from '@/stores/config'
|
||||||
import { dllPrinterReject, dllPrinterReset, openDesignApp } from '@/api/cardsoon'
|
import { dllPrinterEjectCard, dllPrinterReadCard, dllPrinterReject, dllPrinterReset, openDesignApp } from '@/api/cardsoon'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const configStore = useConfigStore()
|
const configStore = useConfigStore()
|
||||||
@@ -73,43 +85,61 @@ function guardInit(action?: string): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function onReset(): Promise<void> {
|
async function onReset(): Promise<void> {
|
||||||
if (!guardInit('重置打印机')) return
|
if (!guardInit(t('common.resetPrinter'))) return
|
||||||
const r = await dllPrinterReset()
|
const r = await dllPrinterReset()
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
notify.success('已发送重置指令')
|
notify.success(t('notify.resetSent'))
|
||||||
await refreshLiveStatus()
|
await refreshLiveStatus()
|
||||||
} else notify.error(r.message || '重置失败')
|
} else notify.error(r.message || t('notify.resetFailed'))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onReject(): Promise<void> {
|
async function onReject(): Promise<void> {
|
||||||
if (!guardInit('废弃卡片')) return
|
if (!guardInit(t('common.discardCard'))) return
|
||||||
if (!configStore.rejectApiAvailable) {
|
if (!configStore.rejectApiAvailable) {
|
||||||
notify.warning('当前环境不支持废卡接口')
|
notify.warning(t('notify.rejectUnavailable'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const r = await dllPrinterReject()
|
const r = await dllPrinterReject()
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
notify.success('已废弃卡片')
|
notify.success(t('notify.cardRejected'))
|
||||||
await refreshLiveStatus()
|
await refreshLiveStatus()
|
||||||
} else notify.error(r.message || '操作失败')
|
} else notify.error(r.message || t('notify.operationFailed'))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onReadCard(): Promise<void> {
|
||||||
|
if (!guardInit(t('home.readCard'))) return
|
||||||
|
const r = await dllPrinterReadCard()
|
||||||
|
if (r.ok) {
|
||||||
|
notify.success(t('notify.readCardSent'))
|
||||||
|
await refreshLiveStatus()
|
||||||
|
} else notify.error(r.message || t('notify.readCardFailed'))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onEjectCard(): Promise<void> {
|
||||||
|
if (!guardInit(t('home.ejectCard'))) return
|
||||||
|
const r = await dllPrinterEjectCard()
|
||||||
|
if (r.ok) {
|
||||||
|
notify.success(t('notify.ejectCardSent'))
|
||||||
|
await refreshLiveStatus()
|
||||||
|
} else notify.error(r.message || t('notify.ejectCardFailed'))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onTemplate(): Promise<void> {
|
async function onTemplate(): Promise<void> {
|
||||||
const r = await openDesignApp()
|
const r = await openDesignApp()
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
notify.error(r.message || '打开设计软件失败,请检查 cardsoon.config.json')
|
notify.warning(r.message || t('notify.designCancelled'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
notify.success('已启动设计软件')
|
notify.success(t('notify.designStarted'))
|
||||||
}
|
}
|
||||||
|
|
||||||
function guardBusy(): boolean {
|
function guardBusy(): boolean {
|
||||||
if (appStore.mode === 'distributing') {
|
if (appStore.mode === 'distributing') {
|
||||||
notify.warning('请先停止数据分发任务')
|
notify.warning(t('notify.stopDistribute'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (appStore.mode === 'usbCopying') {
|
if (appStore.mode === 'usbCopying') {
|
||||||
notify.warning('USB 收集进行中,请等待完成')
|
notify.warning(t('notify.usbCollecting'))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -3,6 +3,19 @@ export interface PrinterStatusSnapshot {
|
|||||||
ribbonAmount: string
|
ribbonAmount: string
|
||||||
statusText: string
|
statusText: string
|
||||||
serialNo: string
|
serialNo: string
|
||||||
|
/** 打印机型号/名称(如 TH80),用于判断单/双面能力 */
|
||||||
|
printerName: string
|
||||||
|
/** 是否为单面打印机(如 TH80),单面打印机不能选"双面"打印 */
|
||||||
|
isSingleSide: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 已知单面打印机型号关键字(命中即视为单面) */
|
||||||
|
const SINGLE_SIDE_PRINTER_PATTERNS = ['TH80']
|
||||||
|
|
||||||
|
function detectSingleSide(printerName: string): boolean {
|
||||||
|
const s = (printerName || '').trim().toUpperCase()
|
||||||
|
if (!s) return false
|
||||||
|
return SINGLE_SIDE_PRINTER_PATTERNS.some((p) => s.includes(p.toUpperCase()))
|
||||||
}
|
}
|
||||||
|
|
||||||
const PRINTER_STATUS_MAP: Record<string, string> = {
|
const PRINTER_STATUS_MAP: Record<string, string> = {
|
||||||
@@ -57,17 +70,27 @@ function snapshotFromRecord(row: Record<string, unknown>): PrinterStatusSnapshot
|
|||||||
'SerialNo',
|
'SerialNo',
|
||||||
'serialNo',
|
'serialNo',
|
||||||
'szPrinterSerial',
|
'szPrinterSerial',
|
||||||
'PrinterSerial',
|
'PrinterSerial'
|
||||||
'PrinterName'
|
])
|
||||||
|
const printerName = pickFirst(row, [
|
||||||
|
'PrinterName',
|
||||||
|
'printer_name',
|
||||||
|
'PrinterModel',
|
||||||
|
'model',
|
||||||
|
'szPrinterName',
|
||||||
|
'szPrinterModel'
|
||||||
])
|
])
|
||||||
const ribbonType = pickFirst(row, ['ribbon_type', 'RibbonType'])
|
const ribbonType = pickFirst(row, ['ribbon_type', 'RibbonType'])
|
||||||
const ribbonAmount = pickFirst(row, ['RibbonAmount', 'ribbon_amount'])
|
const ribbonAmount = pickFirst(row, ['RibbonAmount', 'ribbon_amount'])
|
||||||
|
const nameStr = String(printerName ?? '—')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ribbonType: String(ribbonType ?? '—'),
|
ribbonType: String(ribbonType ?? '—'),
|
||||||
ribbonAmount: String(ribbonAmount ?? '—'),
|
ribbonAmount: String(ribbonAmount ?? '—'),
|
||||||
statusText: '—',
|
statusText: '—',
|
||||||
serialNo: String(serial ?? '—')
|
serialNo: String(serial ?? printerName ?? '—'),
|
||||||
|
printerName: nameStr,
|
||||||
|
isSingleSide: detectSingleSide(nameStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user