Compare commits
49 Commits
5aec2d7d1f
...
online
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2143b8d77d | ||
|
|
237eabc298 | ||
|
|
37b70fe25e | ||
|
|
809c07264f | ||
|
|
a01a36e1b7 | ||
|
|
79f9124c9a | ||
|
|
848cabd0fa | ||
|
|
86a75d5c28 | ||
|
|
660a95c9ed | ||
|
|
ebe08f0c4f | ||
|
|
e5d9cea7b6 | ||
|
|
4d52bceea5 | ||
|
|
3c03907e69 | ||
|
|
fce1099b38 | ||
|
|
9927382054 | ||
|
|
69c1430836 | ||
|
|
d1aca2113d | ||
|
|
a062c996c7 | ||
|
|
d34a501df9 | ||
|
|
947a8a27d3 | ||
|
|
cf269a2f10 | ||
|
|
da317b9460 | ||
|
|
c360d00cac | ||
|
|
60f01ac6d4 | ||
|
|
e9ed63646a | ||
|
|
5d5996b915 | ||
|
|
a54a56865a | ||
|
|
45bd3bc8f5 | ||
|
|
6f3613133b | ||
|
|
7238aabd0a | ||
|
|
3eb98be172 | ||
|
|
63013bbd67 | ||
|
|
ad1cd77a48 | ||
|
|
57ba3a72b8 | ||
|
|
443ee0038b | ||
|
|
33db8595ec | ||
|
|
0dee240c6e | ||
|
|
69f97a740d | ||
|
|
c4303b4d5f | ||
|
|
7b57bb9b05 | ||
|
|
feab964397 | ||
|
|
0320c8d8bc | ||
|
|
2dc2d4887d | ||
|
|
05c6cf6b1c | ||
|
|
722fbd988c | ||
|
|
5554cf1548 | ||
|
|
37369a8e9a | ||
|
|
22747f7c87 | ||
| df67f63137 |
@@ -1,15 +1,19 @@
|
|||||||
# 环境变量 (命名必须以 VITE_ 开头)
|
# 环境变量 (命名必须以 VITE_ 开头)
|
||||||
|
# 接口前缀
|
||||||
# 是否在打包时启用 Mock
|
VITE_API_PREFIX = '/dev-api'
|
||||||
VITE_BUILD_MOCK = false
|
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_BASE_URL = 'https://api.continew.top'
|
VITE_API_BASE_URL = 'http://localhost:6609'
|
||||||
VITE_API_WS_URL = 'wss://api.continew.top'
|
|
||||||
|
# 接口地址 (WebSocket)
|
||||||
|
VITE_API_WS_URL = 'ws://localhost:6609'
|
||||||
|
|
||||||
# 地址前缀
|
# 地址前缀
|
||||||
VITE_BASE = '/'
|
VITE_BASE = '/'
|
||||||
|
|
||||||
|
# 是否开启开发者工具
|
||||||
|
VITE_OPEN_DEVTOOLS = false
|
||||||
|
|
||||||
# 应用配置面板
|
# 应用配置面板
|
||||||
VITE_APP_SETTING = true
|
VITE_APP_SETTING = true
|
||||||
|
|
||||||
|
|||||||
15
.env.test
15
.env.test
@@ -1,22 +1,21 @@
|
|||||||
# 环境变量 (命名必须以 VITE_ 开头)
|
# 环境变量 (命名必须以 VITE_ 开头)
|
||||||
|
|
||||||
# 是否在打包时启用 Mock
|
|
||||||
VITE_BUILD_MOCK = true
|
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_PREFIX = '/test-api'
|
VITE_API_PREFIX = '/dev-api'
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_BASE_URL = 'http://localhost:6609'
|
VITE_API_BASE_URL = 'http://localhost:6609'
|
||||||
|
|
||||||
|
# 接口地址 (WebSocket)
|
||||||
|
VITE_API_WS_URL = 'ws://localhost:6609'
|
||||||
|
|
||||||
# 地址前缀
|
# 地址前缀
|
||||||
VITE_BASE = '/test'
|
VITE_BASE = '/'
|
||||||
|
|
||||||
# 是否开启开发者工具
|
# 是否开启开发者工具
|
||||||
VITE_OPEN_DEVTOOLS = true
|
VITE_OPEN_DEVTOOLS = false
|
||||||
|
|
||||||
# 应用配置面板
|
# 应用配置面板
|
||||||
VITE_APP_SETTING = false
|
VITE_APP_SETTING = true
|
||||||
|
|
||||||
# 终端ID
|
# 终端ID
|
||||||
VITE_CLIENT_ID = 'ef51c9a3e9046c4f2ea45142c8a8344a'
|
VITE_CLIENT_ID = 'ef51c9a3e9046c4f2ea45142c8a8344a'
|
||||||
285
package-lock.json
generated
285
package-lock.json
generated
@@ -37,6 +37,7 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.16",
|
"pinia": "^2.0.16",
|
||||||
"pinia-plugin-persistedstate": "^3.1.0",
|
"pinia-plugin-persistedstate": "^3.1.0",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
"query-string": "^9.0.0",
|
"query-string": "^9.0.0",
|
||||||
"v-viewer": "^3.0.10",
|
"v-viewer": "^3.0.10",
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^20.2.5",
|
"@types/node": "^20.2.5",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/query-string": "^6.3.0",
|
"@types/query-string": "^6.3.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
@@ -3272,6 +3274,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/qrcode": {
|
||||||
|
"version": "1.5.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/qrcode/-/qrcode-1.5.6.tgz",
|
||||||
|
"integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/query-string": {
|
"node_modules/@types/query-string": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/query-string/-/query-string-6.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/query-string/-/query-string-6.3.0.tgz",
|
||||||
@@ -6005,6 +6017,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/decode-uri-component": {
|
"node_modules/decode-uri-component": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
|
"resolved": "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz",
|
||||||
@@ -6154,6 +6175,12 @@
|
|||||||
"url": "https://github.com/sponsors/wooorm"
|
"url": "https://github.com/sponsors/wooorm"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dijkstrajs": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz",
|
||||||
@@ -8447,7 +8474,6 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
"dev": true,
|
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "6.* || 8.* || >= 10.*"
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
@@ -11236,7 +11262,6 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
|
||||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
@@ -11413,7 +11438,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@@ -11555,6 +11579,15 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/posix-character-classes": {
|
"node_modules/posix-character-classes": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||||
@@ -11941,6 +11974,233 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dijkstrajs": "^1.0.1",
|
||||||
|
"pngjs": "^5.0.0",
|
||||||
|
"yargs": "^15.3.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"qrcode": "bin/qrcode"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/y18n": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/yargs": {
|
||||||
|
"version": "15.4.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
|
||||||
|
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"decamelize": "^1.2.0",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^18.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/qrcode/node_modules/yargs-parser": {
|
||||||
|
"version": "18.1.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||||
|
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
|
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
|
||||||
@@ -12335,7 +12595,6 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
@@ -12352,6 +12611,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/resize-detector": {
|
"node_modules/resize-detector": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/resize-detector/-/resize-detector-0.3.0.tgz",
|
||||||
@@ -12875,6 +13140,12 @@
|
|||||||
"randombytes": "^2.1.0"
|
"randombytes": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
@@ -16154,6 +16425,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-module": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/which-typed-array": {
|
"node_modules/which-typed-array": {
|
||||||
"version": "1.1.19",
|
"version": "1.1.19",
|
||||||
"resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
"resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||||
|
|||||||
@@ -43,6 +43,7 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.16",
|
"pinia": "^2.0.16",
|
||||||
"pinia-plugin-persistedstate": "^3.1.0",
|
"pinia-plugin-persistedstate": "^3.1.0",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
"query-string": "^9.0.0",
|
"query-string": "^9.0.0",
|
||||||
"v-viewer": "^3.0.10",
|
"v-viewer": "^3.0.10",
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^20.2.5",
|
"@types/node": "^20.2.5",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/query-string": "^6.3.0",
|
"@types/query-string": "^6.3.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
|
|||||||
@@ -83,11 +83,6 @@ export interface CardLoginReq extends AuthReq {
|
|||||||
cardNumber: string
|
cardNumber: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 邮箱登录请求参数 */
|
|
||||||
export interface CardLoginReq extends AuthReq {
|
|
||||||
card: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 登录响应类型 */
|
/** 登录响应类型 */
|
||||||
export interface LoginResp {
|
export interface LoginResp {
|
||||||
token: string
|
token: string
|
||||||
|
|||||||
61
src/apis/fullWorkOrder/fullWorkOrder.ts
Normal file
61
src/apis/fullWorkOrder/fullWorkOrder.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
|
const BASE_URL = '/fullWorkOrder/fullWorkOrder'
|
||||||
|
|
||||||
|
export interface FullWorkOrderResp {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
orderNo: string
|
||||||
|
materialCode: string
|
||||||
|
materialName: string
|
||||||
|
batch: string
|
||||||
|
mark: string
|
||||||
|
count: number
|
||||||
|
imgUrl: string
|
||||||
|
createUser: string
|
||||||
|
createTime: string
|
||||||
|
updateUser: string
|
||||||
|
updateTime: string
|
||||||
|
createUserString: string
|
||||||
|
updateUserString: string
|
||||||
|
disabled: boolean
|
||||||
|
}
|
||||||
|
export interface FullWorkOrderQuery {
|
||||||
|
orderNo: string | undefined
|
||||||
|
materialCode: string | undefined
|
||||||
|
materialName: string | undefined
|
||||||
|
batch: string | undefined
|
||||||
|
createTime: Array<string> | undefined
|
||||||
|
sort: Array<string>
|
||||||
|
}
|
||||||
|
export interface FullWorkOrderPageQuery extends FullWorkOrderQuery, PageQuery {}
|
||||||
|
|
||||||
|
/** @desc 查询整箱领取记录列表 */
|
||||||
|
export function listFullWorkOrder(query: FullWorkOrderPageQuery) {
|
||||||
|
return http.get<PageRes<FullWorkOrderResp[]>>(`${BASE_URL}`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 新增整箱领取记录 */
|
||||||
|
export function addFullWorkOrder(data: any) {
|
||||||
|
return http.post(`${BASE_URL}`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 删除整箱领取记录 */
|
||||||
|
export function deleteFullWorkOrder(id: string) {
|
||||||
|
return http.del(`${BASE_URL}/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导出整箱领取记录 */
|
||||||
|
export function exportFullWorkOrder(query: FullWorkOrderQuery) {
|
||||||
|
return http.download(`${BASE_URL}/export`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 保存原材料详情 */
|
||||||
|
export function saveFullWorkOrderDetail(data: any) {
|
||||||
|
return http.post(`${BASE_URL}/saveInfo`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 获取原材料详情列表 */
|
||||||
|
export function getFullWorkOrderDetailList(id: string) {
|
||||||
|
return http.get(`${BASE_URL}/infos/${id}`)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import http from '@/utils/http'
|
|||||||
const BASE_URL = '/admin/materialInfo'
|
const BASE_URL = '/admin/materialInfo'
|
||||||
|
|
||||||
export interface MaterialInfoResp {
|
export interface MaterialInfoResp {
|
||||||
|
id: string
|
||||||
materialName: string
|
materialName: string
|
||||||
encoding: string
|
encoding: string
|
||||||
unitWeight: string
|
unitWeight: string
|
||||||
@@ -12,11 +13,17 @@ export interface MaterialInfoResp {
|
|||||||
createTime: string
|
createTime: string
|
||||||
createUserString: string
|
createUserString: string
|
||||||
updateUserString: string
|
updateUserString: string
|
||||||
|
lightLevel: number
|
||||||
|
batch: string
|
||||||
|
mark: string
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
|
photoLoadError: boolean
|
||||||
}
|
}
|
||||||
export interface MaterialInfoQuery {
|
export interface MaterialInfoQuery {
|
||||||
materialName: string | undefined
|
materialName: string | undefined
|
||||||
encoding: string | undefined
|
encoding: string | undefined
|
||||||
|
batch: string | undefined
|
||||||
|
mark: string | undefined
|
||||||
sort: Array<string>
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,9 +42,6 @@ export function listMaterialInfo(query: MaterialInfoPageQuery) {
|
|||||||
return http.get<PageRes<MaterialInfoResp[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<MaterialInfoResp[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MaterialInfoDetailResp {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @desc 下载物料信息导入模板 */
|
/** @desc 下载物料信息导入模板 */
|
||||||
export function downloadMaterialInfoImportTemplate() {
|
export function downloadMaterialInfoImportTemplate() {
|
||||||
return http.download(`${BASE_URL}/import/template`)
|
return http.download(`${BASE_URL}/import/template`)
|
||||||
@@ -45,7 +49,7 @@ export function downloadMaterialInfoImportTemplate() {
|
|||||||
|
|
||||||
/** @desc 查询物料信息详情 */
|
/** @desc 查询物料信息详情 */
|
||||||
export function getMaterialInfo(id: string) {
|
export function getMaterialInfo(id: string) {
|
||||||
return http.get<MaterialInfoDetailResp>(`${BASE_URL}/${id}`)
|
return http.get<MaterialInfoResp>(`${BASE_URL}/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 新增物料信息 */
|
/** @desc 新增物料信息 */
|
||||||
@@ -59,7 +63,7 @@ export function updateMaterialInfo(data: any, id: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 删除物料信息 */
|
/** @desc 删除物料信息 */
|
||||||
export function deleteMaterialInfo(id: string) {
|
export function deleteMaterialInfo(id: string | Array<string>) {
|
||||||
return http.del(`${BASE_URL}/${id}`)
|
return http.del(`${BASE_URL}/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,3 +91,26 @@ export function uploadMaterialPhotos(data: FormData) {
|
|||||||
export function catchPhoto(data: FormData) {
|
export function catchPhoto(data: FormData) {
|
||||||
return http.post(`${BASE_URL}/import/catch`, data)
|
return http.post(`${BASE_URL}/import/catch`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 批次导入结果类型 */
|
||||||
|
export interface BatchImportResp {
|
||||||
|
importKey: string
|
||||||
|
totalRows: number
|
||||||
|
validRows: number
|
||||||
|
duplicateRows: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 下载批次导入模板 */
|
||||||
|
export function downloadBatchImportTemplate() {
|
||||||
|
return http.download(`${BASE_URL}/batch/import/template`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 解析批次导入数据 */
|
||||||
|
export function parseBatchImport(data: FormData) {
|
||||||
|
return http.post(`${BASE_URL}/batch/import/parse`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 批次导入 */
|
||||||
|
export function batchImport(data: any) {
|
||||||
|
return http.post(`${BASE_URL}/batch/import`, data)
|
||||||
|
}
|
||||||
|
|||||||
68
src/apis/materialProcess/materialProcess.ts
Normal file
68
src/apis/materialProcess/materialProcess.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
import type {LabelValueState} from "@/types/global";
|
||||||
|
|
||||||
|
const BASE_URL = '/materialProcess/materialProcess'
|
||||||
|
|
||||||
|
export interface MaterialProcessResp {
|
||||||
|
id: string
|
||||||
|
processName: string
|
||||||
|
processCode: string
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
createUser: string
|
||||||
|
updateUser: string
|
||||||
|
createUserString: string
|
||||||
|
updateUserString: string
|
||||||
|
disabled: boolean
|
||||||
|
}
|
||||||
|
export interface MaterialProcessQuery {
|
||||||
|
processName: string | undefined
|
||||||
|
processCode: string | undefined
|
||||||
|
sort: Array<string>
|
||||||
|
}
|
||||||
|
export interface MaterialProcessPageQuery extends MaterialProcessQuery, PageQuery {}
|
||||||
|
|
||||||
|
/** @desc 查询海康物料流程列表 */
|
||||||
|
export function listMaterialProcess(query: MaterialProcessPageQuery) {
|
||||||
|
return http.get<PageRes<MaterialProcessResp[]>>(`${BASE_URL}`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 查询海康物料流程详情 */
|
||||||
|
export function selectList() {
|
||||||
|
return http.get(`${BASE_URL}/selectList`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 新增海康物料流程 */
|
||||||
|
export function addMaterialProcess(data: any) {
|
||||||
|
return http.post(`${BASE_URL}`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 修改海康物料流程 */
|
||||||
|
export function updateMaterialProcess(data: any, id: string) {
|
||||||
|
return http.put(`${BASE_URL}/${id}`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 删除海康物料流程 */
|
||||||
|
export function deleteMaterialProcess(id: string | Array<string>) {
|
||||||
|
return http.del(`${BASE_URL}/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导出海康物料流程 */
|
||||||
|
export function exportMaterialProcess(query: MaterialProcessQuery) {
|
||||||
|
return http.download(`${BASE_URL}/export`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 下载物料流程导入模板 */
|
||||||
|
export function downloadMaterialProcessImportTemplate() {
|
||||||
|
return http.download(`${BASE_URL}/import/template`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 解析物料流程导入数据 */
|
||||||
|
export function parseImportMaterialProcess(data: FormData) {
|
||||||
|
return http.post(`${BASE_URL}/import/parse`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导入物料流程 */
|
||||||
|
export function importMaterialProcess(data: any) {
|
||||||
|
return http.post(`${BASE_URL}/import`, data)
|
||||||
|
}
|
||||||
80
src/apis/materialType/materialType.ts
Normal file
80
src/apis/materialType/materialType.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
|
const BASE_URL = '/materialType/materialType'
|
||||||
|
|
||||||
|
export interface MaterialTypeResp {
|
||||||
|
id: string
|
||||||
|
typeName: string
|
||||||
|
upFloatRatio: string
|
||||||
|
downFloatRatio: string
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
createUser: string
|
||||||
|
updateUser: string
|
||||||
|
createUserString: string
|
||||||
|
updateUserString: string
|
||||||
|
disabled: boolean
|
||||||
|
}
|
||||||
|
export interface MaterialTypeQuery {
|
||||||
|
typeName: string | undefined
|
||||||
|
sort: Array<string>
|
||||||
|
}
|
||||||
|
export interface MaterialTypePageQuery extends MaterialTypeQuery, PageQuery {}
|
||||||
|
|
||||||
|
export interface MaterialTypeImportResp {
|
||||||
|
importKey: string
|
||||||
|
totalRows: number
|
||||||
|
validRows: number
|
||||||
|
duplicateNameRows: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MaterialTypeImportResult {
|
||||||
|
insertRows: number
|
||||||
|
updateRows: number
|
||||||
|
totalRows: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 查询物料品类列表 */
|
||||||
|
export function listMaterialType(query: MaterialTypePageQuery) {
|
||||||
|
return http.get<PageRes<MaterialTypeResp[]>>(`${BASE_URL}`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 新增物料品类 */
|
||||||
|
export function addMaterialType(data: any) {
|
||||||
|
return http.post(`${BASE_URL}`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 查询物料品类 */
|
||||||
|
export function selectList() {
|
||||||
|
return http.get(`${BASE_URL}/selectList`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 修改物料品类 */
|
||||||
|
export function updateMaterialType(data: any, id: string) {
|
||||||
|
return http.put(`${BASE_URL}/${id}`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 删除物料品类 */
|
||||||
|
export function deleteMaterialType(id: string) {
|
||||||
|
return http.del(`${BASE_URL}/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导出物料品类 */
|
||||||
|
export function exportMaterialType(query: MaterialTypeQuery) {
|
||||||
|
return http.download(`${BASE_URL}/export`, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 下载物料品类导入模板 */
|
||||||
|
export function downloadMaterialTypeImportTemplate() {
|
||||||
|
return http.download(`${BASE_URL}/importTemplate`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 解析物料品类导入数据 */
|
||||||
|
export function parseImportMaterialType(data: FormData) {
|
||||||
|
return http.post<MaterialTypeImportResp>(`${BASE_URL}/parseImport`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 导入物料品类 */
|
||||||
|
export function importMaterialType(data: any) {
|
||||||
|
return http.post<MaterialTypeImportResult>(`${BASE_URL}/import`, data)
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ export interface UserResp {
|
|||||||
email: string
|
email: string
|
||||||
phone: string
|
phone: string
|
||||||
description: string
|
description: string
|
||||||
|
dataSource: number
|
||||||
status: 1 | 2
|
status: 1 | 2
|
||||||
isSystem?: boolean
|
isSystem?: boolean
|
||||||
createUserString: string
|
createUserString: string
|
||||||
|
|||||||
23
src/apis/weightManage/light.ts
Normal file
23
src/apis/weightManage/light.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
|
const BASE_URL = '/api/light'
|
||||||
|
|
||||||
|
/** @desc 连接灯光 */
|
||||||
|
export function connect() {
|
||||||
|
return http.post<any>(`${BASE_URL}/connect`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 断开灯光连接 */
|
||||||
|
export function disconnect() {
|
||||||
|
return http.post<any>(`${BASE_URL}/disconnect`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 设置灯光亮度 */
|
||||||
|
export function brightness(materialId: string) {
|
||||||
|
return http.post<any>(`${BASE_URL}/brightness`, { materialId: Number(materialId) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 检查灯光状态 */
|
||||||
|
export function status() {
|
||||||
|
return http.get<any>(`${BASE_URL}/status`)
|
||||||
|
}
|
||||||
@@ -9,7 +9,12 @@ export interface WeighManageResp {
|
|||||||
materialSpec: string
|
materialSpec: string
|
||||||
unitWeight: number
|
unitWeight: number
|
||||||
photoUrl: string
|
photoUrl: string
|
||||||
|
batch: string
|
||||||
|
mark: string
|
||||||
|
materialProcess: string
|
||||||
matchResult: string
|
matchResult: string
|
||||||
|
downFloatRatio: string
|
||||||
|
upFloatRatio: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WeighManageQuery {
|
export interface WeighManageQuery {
|
||||||
@@ -26,12 +31,27 @@ export function validateWeighing(data: any) {
|
|||||||
return http.post(`${BASE_URL}/validateWeighing`, data)
|
return http.post(`${BASE_URL}/validateWeighing`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 校验称重信息 */
|
/** @desc 校验物料是否一致 */
|
||||||
export function vmSend(code: string) {
|
export function vmSend(materialCode: string) {
|
||||||
return http.get<WeighManageResp>(`/vm/send?msg=${code}`)
|
return http.post<string>(`/vm/send`, { materialCode })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 获取图片 */
|
/** @desc vm定时任务保存图片 */
|
||||||
export function getImg() {
|
export function getVmSaveImageTask() {
|
||||||
return http.get<string>(`/vm/latest-photo`)
|
return http.post<any>(`/vm/start`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc vm关闭定时任务 */
|
||||||
|
export function getVmCloseTask() {
|
||||||
|
return http.post<any>(`/vm/stop`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 启动电子称连接线程 */
|
||||||
|
export function weighAHStart() {
|
||||||
|
return http.post<any>(`/api/weigh/ah/init`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 终止电子称连接线程 */
|
||||||
|
export function weighAHStop() {
|
||||||
|
return http.post<any>(`/api/weigh/ah/destroy`)
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/apis/weightManage/ys.ts
Normal file
23
src/apis/weightManage/ys.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
|
const BASE_URL = '/api/ys'
|
||||||
|
|
||||||
|
/** @desc 启动宇视SDK */
|
||||||
|
export function getEnterWeighPage() {
|
||||||
|
return http.get<any>(`${BASE_URL}/enter-weigh-page`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 退出宇视SDK */
|
||||||
|
export function getLeaveWeighPage() {
|
||||||
|
return http.get<any>(`${BASE_URL}/leave-weigh-page`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 抓拍图片 */
|
||||||
|
export function getCaptureImage(data: any) {
|
||||||
|
return http.get<any>(`${BASE_URL}/capture-image`, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @desc 检查宇视SDK状态 */
|
||||||
|
export function getCheckStatus() {
|
||||||
|
return http.get<any>(`${BASE_URL}/status`)
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ export interface WorkOrderResp {
|
|||||||
unitWeight: string
|
unitWeight: string
|
||||||
materialSpec: string
|
materialSpec: string
|
||||||
photoUrl: string
|
photoUrl: string
|
||||||
|
batch: string
|
||||||
totalWeight: string
|
totalWeight: string
|
||||||
totalCalculatedWeight: string
|
totalCalculatedWeight: string
|
||||||
totalCount: string
|
totalCount: string
|
||||||
@@ -19,6 +20,7 @@ export interface WorkOrderResp {
|
|||||||
matchResult: string
|
matchResult: string
|
||||||
workOrderInfos: Array<WorkOrderInfoResp>
|
workOrderInfos: Array<WorkOrderInfoResp>
|
||||||
qrCodeData: string
|
qrCodeData: string
|
||||||
|
mark: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkOrderInfoResp {
|
export interface WorkOrderInfoResp {
|
||||||
@@ -26,15 +28,19 @@ export interface WorkOrderInfoResp {
|
|||||||
workOrderId: string
|
workOrderId: string
|
||||||
materialId: string
|
materialId: string
|
||||||
weightTime: string
|
weightTime: string
|
||||||
|
batch: string
|
||||||
quantity: string
|
quantity: string
|
||||||
weight: string
|
weight: string
|
||||||
imgUrl: string
|
imgUrl: string
|
||||||
calculatedWeight: string
|
calculatedWeight: string
|
||||||
|
weightQuantity: string
|
||||||
|
mark: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkOrderQuery {
|
export interface WorkOrderQuery {
|
||||||
orderNo: string | undefined
|
orderNo: string | undefined
|
||||||
materialName: string | undefined
|
materialName: string | undefined
|
||||||
|
batch: string | undefined
|
||||||
encoding: string | undefined
|
encoding: string | undefined
|
||||||
userName: string | undefined
|
userName: string | undefined
|
||||||
carNo: string | undefined
|
carNo: string | undefined
|
||||||
|
|||||||
BIN
src/assets/wav/tooLess.wav
Normal file
BIN
src/assets/wav/tooLess.wav
Normal file
Binary file not shown.
BIN
src/assets/wav/tooMany.wav
Normal file
BIN
src/assets/wav/tooMany.wav
Normal file
Binary file not shown.
@@ -38,7 +38,7 @@ export const FileIcon: FileExtendNameIconMap = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 图片类型 */
|
/** 图片类型 */
|
||||||
export const ImageTypes = ['jpg', 'png', 'gif', 'jpeg']
|
export const ImageTypes = ['jpg', 'png', 'gif', 'jpeg', 'bmp']
|
||||||
|
|
||||||
/** WPS、Office文件类型 */
|
/** WPS、Office文件类型 */
|
||||||
export const OfficeTypes = ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf']
|
export const OfficeTypes = ['ppt', 'pptx', 'doc', 'docx', 'xls', 'xlsx', 'pdf']
|
||||||
|
|||||||
22
src/hooks/app/materialProcess.ts
Normal file
22
src/hooks/app/materialProcess.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { selectList } from '@/apis/materialProcess/materialProcess'
|
||||||
|
import type { LabelValueState } from '@/types/global'
|
||||||
|
|
||||||
|
/** 物料品类模块 */
|
||||||
|
export function materialProcess(options?: { onSuccess?: () => void }) {
|
||||||
|
const loading = ref(false)
|
||||||
|
const materialProcessList = ref<LabelValueState[]>([])
|
||||||
|
|
||||||
|
const getMaterialProcessSelect = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const res = await selectList()
|
||||||
|
materialProcessList.value = res.data
|
||||||
|
// eslint-disable-next-line ts/no-unused-expressions
|
||||||
|
options?.onSuccess && options.onSuccess()
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { materialProcessList, getMaterialProcessSelect, loading }
|
||||||
|
}
|
||||||
22
src/hooks/app/materialType.ts
Normal file
22
src/hooks/app/materialType.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { selectList } from '@/apis/materialType/materialType'
|
||||||
|
import type { LabelValueState } from '@/types/global'
|
||||||
|
|
||||||
|
/** 物料品类模块 */
|
||||||
|
export function materialType(options?: { onSuccess?: () => void }) {
|
||||||
|
const loading = ref(false)
|
||||||
|
const materialTypeList = ref<LabelValueState[]>([])
|
||||||
|
|
||||||
|
const getMaterialTypeSelect = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const res = await selectList()
|
||||||
|
materialTypeList.value = res.data
|
||||||
|
// eslint-disable-next-line ts/no-unused-expressions
|
||||||
|
options?.onSuccess && options.onSuccess()
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { materialTypeList, getMaterialTypeSelect, loading }
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||||||
path: '/',
|
path: '/',
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: '/dashboard/analysis', // 改为跳转到分析页
|
redirect: '/workOrder', // 改为跳转到工作订单页
|
||||||
meta: { title: '仪表盘', icon: 'dashboard', hidden: false },
|
meta: { title: '仪表盘', icon: 'dashboard', hidden: false },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -25,9 +25,9 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||||||
meta: { title: '工作台', icon: 'desktop', hidden: true }, // 改为隐藏
|
meta: { title: '工作台', icon: 'desktop', hidden: true }, // 改为隐藏
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dashboard/analysis',
|
path: '/workOrder',
|
||||||
name: 'Analysis',
|
name: 'WorkOrder',
|
||||||
component: () => import('@/views/dashboard/analysis/index.vue'),
|
component: () => import('@/views/workOrder/index.vue'),
|
||||||
meta: { title: '首页', icon: 'insert-chart', hidden: false, affix: true },
|
meta: { title: '首页', icon: 'insert-chart', hidden: false, affix: true },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
20
src/types/components.d.ts
vendored
20
src/types/components.d.ts
vendored
@@ -13,8 +13,15 @@ declare module 'vue' {
|
|||||||
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
ABreadcrumb: typeof import('@arco-design/web-vue')['Breadcrumb']
|
||||||
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
ABreadcrumbItem: typeof import('@arco-design/web-vue')['BreadcrumbItem']
|
||||||
AButton: typeof import('@arco-design/web-vue')['Button']
|
AButton: typeof import('@arco-design/web-vue')['Button']
|
||||||
|
AButtonGroup: typeof import('@arco-design/web-vue')['ButtonGroup']
|
||||||
|
ACard: typeof import('@arco-design/web-vue')['Card']
|
||||||
|
ACardMeta: typeof import('@arco-design/web-vue')['CardMeta']
|
||||||
|
ACarousel: typeof import('@arco-design/web-vue')['Carousel']
|
||||||
|
ACarouselItem: typeof import('@arco-design/web-vue')['CarouselItem']
|
||||||
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
ACheckbox: typeof import('@arco-design/web-vue')['Checkbox']
|
||||||
|
ACheckboxGroup: typeof import('@arco-design/web-vue')['CheckboxGroup']
|
||||||
ACol: typeof import('@arco-design/web-vue')['Col']
|
ACol: typeof import('@arco-design/web-vue')['Col']
|
||||||
|
AColorPicker: typeof import('@arco-design/web-vue')['ColorPicker']
|
||||||
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
AConfigProvider: typeof import('@arco-design/web-vue')['ConfigProvider']
|
||||||
ADatePicker: typeof import('@arco-design/web-vue')['DatePicker']
|
ADatePicker: typeof import('@arco-design/web-vue')['DatePicker']
|
||||||
ADescriptions: typeof import('@arco-design/web-vue')['Descriptions']
|
ADescriptions: typeof import('@arco-design/web-vue')['Descriptions']
|
||||||
@@ -28,21 +35,24 @@ declare module 'vue' {
|
|||||||
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
AFormItem: typeof import('@arco-design/web-vue')['FormItem']
|
||||||
AGrid: typeof import('@arco-design/web-vue')['Grid']
|
AGrid: typeof import('@arco-design/web-vue')['Grid']
|
||||||
AGridItem: typeof import('@arco-design/web-vue')['GridItem']
|
AGridItem: typeof import('@arco-design/web-vue')['GridItem']
|
||||||
AIcon: typeof import('@arco-design/web-vue')['Icon']
|
|
||||||
AImage: typeof import('@arco-design/web-vue')['Image']
|
AImage: typeof import('@arco-design/web-vue')['Image']
|
||||||
AInput: typeof import('@arco-design/web-vue')['Input']
|
AInput: typeof import('@arco-design/web-vue')['Input']
|
||||||
|
AInputGroup: typeof import('@arco-design/web-vue')['InputGroup']
|
||||||
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
AInputNumber: typeof import('@arco-design/web-vue')['InputNumber']
|
||||||
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
AInputPassword: typeof import('@arco-design/web-vue')['InputPassword']
|
||||||
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
AInputSearch: typeof import('@arco-design/web-vue')['InputSearch']
|
||||||
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
ALayout: typeof import('@arco-design/web-vue')['Layout']
|
||||||
|
ALayoutContent: typeof import('@arco-design/web-vue')['LayoutContent']
|
||||||
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
ALayoutHeader: typeof import('@arco-design/web-vue')['LayoutHeader']
|
||||||
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
ALayoutSider: typeof import('@arco-design/web-vue')['LayoutSider']
|
||||||
ALink: typeof import('@arco-design/web-vue')['Link']
|
ALink: typeof import('@arco-design/web-vue')['Link']
|
||||||
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
AMenu: typeof import('@arco-design/web-vue')['Menu']
|
||||||
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
AMenuItem: typeof import('@arco-design/web-vue')['MenuItem']
|
||||||
AModal: typeof import('@arco-design/web-vue')['Modal']
|
AModal: typeof import('@arco-design/web-vue')['Modal']
|
||||||
|
AOption: typeof import('@arco-design/web-vue')['Option']
|
||||||
AOverflowList: typeof import('@arco-design/web-vue')['OverflowList']
|
AOverflowList: typeof import('@arco-design/web-vue')['OverflowList']
|
||||||
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
APagination: typeof import('@arco-design/web-vue')['Pagination']
|
||||||
|
APopconfirm: typeof import('@arco-design/web-vue')['Popconfirm']
|
||||||
APopover: typeof import('@arco-design/web-vue')['Popover']
|
APopover: typeof import('@arco-design/web-vue')['Popover']
|
||||||
AProgress: typeof import('@arco-design/web-vue')['Progress']
|
AProgress: typeof import('@arco-design/web-vue')['Progress']
|
||||||
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
ARadio: typeof import('@arco-design/web-vue')['Radio']
|
||||||
@@ -51,18 +61,26 @@ declare module 'vue' {
|
|||||||
ARow: typeof import('@arco-design/web-vue')['Row']
|
ARow: typeof import('@arco-design/web-vue')['Row']
|
||||||
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
AScrollbar: typeof import('@arco-design/web-vue')['Scrollbar']
|
||||||
ASelect: typeof import('@arco-design/web-vue')['Select']
|
ASelect: typeof import('@arco-design/web-vue')['Select']
|
||||||
|
ASkeleton: typeof import('@arco-design/web-vue')['Skeleton']
|
||||||
|
ASkeletonLine: typeof import('@arco-design/web-vue')['SkeletonLine']
|
||||||
ASpace: typeof import('@arco-design/web-vue')['Space']
|
ASpace: typeof import('@arco-design/web-vue')['Space']
|
||||||
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
ASpin: typeof import('@arco-design/web-vue')['Spin']
|
||||||
AStatistic: typeof import('@arco-design/web-vue')['Statistic']
|
AStatistic: typeof import('@arco-design/web-vue')['Statistic']
|
||||||
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
|
ASubMenu: typeof import('@arco-design/web-vue')['SubMenu']
|
||||||
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
ASwitch: typeof import('@arco-design/web-vue')['Switch']
|
||||||
ATable: typeof import('@arco-design/web-vue')['Table']
|
ATable: typeof import('@arco-design/web-vue')['Table']
|
||||||
|
ATableColumn: typeof import('@arco-design/web-vue')['TableColumn']
|
||||||
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
ATabPane: typeof import('@arco-design/web-vue')['TabPane']
|
||||||
ATabs: typeof import('@arco-design/web-vue')['Tabs']
|
ATabs: typeof import('@arco-design/web-vue')['Tabs']
|
||||||
ATag: typeof import('@arco-design/web-vue')['Tag']
|
ATag: typeof import('@arco-design/web-vue')['Tag']
|
||||||
|
ATextarea: typeof import('@arco-design/web-vue')['Textarea']
|
||||||
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
ATooltip: typeof import('@arco-design/web-vue')['Tooltip']
|
||||||
|
ATree: typeof import('@arco-design/web-vue')['Tree']
|
||||||
ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect']
|
ATreeSelect: typeof import('@arco-design/web-vue')['TreeSelect']
|
||||||
|
ATrigger: typeof import('@arco-design/web-vue')['Trigger']
|
||||||
ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph']
|
ATypographyParagraph: typeof import('@arco-design/web-vue')['TypographyParagraph']
|
||||||
|
ATypographyText: typeof import('@arco-design/web-vue')['TypographyText']
|
||||||
|
ATypographyTitle: typeof import('@arco-design/web-vue')['TypographyTitle']
|
||||||
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
AUpload: typeof import('@arco-design/web-vue')['Upload']
|
||||||
Avatar: typeof import('./../components/Avatar/index.vue')['default']
|
Avatar: typeof import('./../components/Avatar/index.vue')['default']
|
||||||
AWatermark: typeof import('@arco-design/web-vue')['Watermark']
|
AWatermark: typeof import('@arco-design/web-vue')['Watermark']
|
||||||
|
|||||||
25
src/utils/qrCodeGenerator.js
Normal file
25
src/utils/qrCodeGenerator.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import QRCode from 'qrcode'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成二维码Base64图片
|
||||||
|
* @param {string} data - 要编码的二维码数据
|
||||||
|
* @param {Object} options - 二维码选项
|
||||||
|
* @returns {Promise<string>} Base64编码的二维码图片
|
||||||
|
*/
|
||||||
|
export async function generateQRCode(data, options = {}) {
|
||||||
|
try {
|
||||||
|
const defaultOptions = {
|
||||||
|
width: 120,
|
||||||
|
margin: 1,
|
||||||
|
color: {
|
||||||
|
dark: '#000000',
|
||||||
|
light: '#FFFFFF',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const finalOptions = { ...defaultOptions, ...options }
|
||||||
|
return await QRCode.toDataURL(data, finalOptions)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,23 +23,94 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-grid-item">
|
<div class="form-grid-item">
|
||||||
<a-form-item label="生产批次" required>
|
<a-form-item label="生产批次">
|
||||||
<a-input v-model="formData.productionBatch" placeholder="请输入生产批次" />
|
<a-input v-model="formData.batch" placeholder="未获取到生产批次" :disabled="true" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-actions">
|
|
||||||
<a-button type="primary" @click="generateLabel" :disabled="!formData.productionBatch">生成标签</a-button>
|
|
||||||
</div>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
|
<div class="form-actions">
|
||||||
|
<a-button type="primary" @click="generateDetailLabel">明细标签</a-button>
|
||||||
|
<a-button type="primary" @click="generateOverallLabel">整体标签</a-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 标签预览 -->
|
<!-- 标签预览 -->
|
||||||
<div v-if="labelData.partName" class="label-preview-section">
|
<div v-if="labelDataList.length > 0 || labelData.partName" class="label-preview-section">
|
||||||
<!-- <div class="label-preview-section">-->
|
|
||||||
<h3>标签预览</h3>
|
<h3>标签预览</h3>
|
||||||
<div class="label-container" ref="labelContainer">
|
<div class="label-container" ref="labelContainer">
|
||||||
<div class="label" v-for="index in 1" :key="index">
|
<!-- 明细标签:显示多个标签 -->
|
||||||
|
<template v-if="labelDataList.length > 0">
|
||||||
|
<div class="label" v-for="(item, index) in labelDataList" :key="index">
|
||||||
|
<table class="label-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件名称</div>
|
||||||
|
<div class="label-value">{{ item.partName }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">生产日期</div>
|
||||||
|
<div class="label-value">{{ item.productionDate }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img v-if="item.qrCodeImage" :src="item.qrCodeImage" alt="QR Code" />
|
||||||
|
<div class="mark-number">{{ item.mark || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件号</div>
|
||||||
|
<div class="label-value">{{ item.partNumber }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">数量</div>
|
||||||
|
<div class="label-value">{{ item.totalCount }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">标重(g)</div>
|
||||||
|
<div class="label-value">{{ item.totalCalculatedWeight }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">包装签字</div>
|
||||||
|
<div class="label-value">{{ item.packingSignature || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">实重(g)</div>
|
||||||
|
<div class="label-value">{{ item.totalWeight || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">检验签字</div>
|
||||||
|
<div class="label-value">{{ item.inspectionSignature || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- 整体标签:显示单个标签 -->
|
||||||
|
<template v-else>
|
||||||
|
<div class="label">
|
||||||
<table class="label-table">
|
<table class="label-table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label-cell">
|
<td class="label-cell">
|
||||||
@@ -56,7 +127,8 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="label-cell qr-cell" rowspan="4">
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
<div class="qr-code">
|
<div class="qr-code">
|
||||||
<img :src="`https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=${encodeURIComponent(labelData.qrCodeData)}`" alt="QR Code" />
|
<img v-if="labelData.qrCodeImage" :src="labelData.qrCodeImage" alt="QR Code" />
|
||||||
|
<div class="mark-number">{{ labelData.mark || '' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -77,7 +149,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="label-cell">
|
<td class="label-cell">
|
||||||
<div class="label-row">
|
<div class="label-row">
|
||||||
<div class="label-field">标重(kg)</div>
|
<div class="label-field">标重(g)</div>
|
||||||
<div class="label-value">{{ labelData.totalCalculatedWeight }}</div>
|
<div class="label-value">{{ labelData.totalCalculatedWeight }}</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -91,7 +163,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td class="label-cell">
|
<td class="label-cell">
|
||||||
<div class="label-row">
|
<div class="label-row">
|
||||||
<div class="label-field">实重(kg)</div>
|
<div class="label-field">实重(g)</div>
|
||||||
<div class="label-value">{{ labelData.totalWeight || '' }}</div>
|
<div class="label-value">{{ labelData.totalWeight || '' }}</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -104,6 +176,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="label-actions">
|
<div class="label-actions">
|
||||||
<a-button type="primary" @click="printLabel">打印标签</a-button>
|
<a-button type="primary" @click="printLabel">打印标签</a-button>
|
||||||
@@ -117,7 +190,8 @@
|
|||||||
import { ref, reactive, nextTick, onMounted } from 'vue'
|
import { ref, reactive, nextTick, onMounted } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import {getWorkOrder} from "@/apis/workOrder/workOrder";
|
import {getWorkOrder, type WorkOrderInfoResp} from "@/apis/workOrder/workOrder"
|
||||||
|
import QRCode from 'qrcode';
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -130,11 +204,28 @@ const formData = reactive({
|
|||||||
totalCalculatedWeight: '',
|
totalCalculatedWeight: '',
|
||||||
totalWeight: '',
|
totalWeight: '',
|
||||||
totalCount: '',
|
totalCount: '',
|
||||||
productionBatch: '',
|
batch: '',
|
||||||
|
mark: '',
|
||||||
qrCodeData: '',
|
qrCodeData: '',
|
||||||
|
workOrderInfos: Array<WorkOrderInfoResp>(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// 标签数据
|
// 标签数据数组
|
||||||
|
const labelDataList = reactive<Array<{
|
||||||
|
partName: string
|
||||||
|
partNumber: string
|
||||||
|
totalCalculatedWeight: string
|
||||||
|
totalWeight: string
|
||||||
|
productionDate: string
|
||||||
|
totalCount: string
|
||||||
|
packingSignature: string
|
||||||
|
inspectionSignature: string
|
||||||
|
qrCodeData: string
|
||||||
|
qrCodeImage: string
|
||||||
|
mark: string
|
||||||
|
}>>([])
|
||||||
|
|
||||||
|
// 标签数据(用于整体标签)
|
||||||
const labelData = reactive({
|
const labelData = reactive({
|
||||||
partName: '',
|
partName: '',
|
||||||
partNumber: '',
|
partNumber: '',
|
||||||
@@ -145,28 +236,40 @@ const labelData = reactive({
|
|||||||
packingSignature: '',
|
packingSignature: '',
|
||||||
inspectionSignature: '',
|
inspectionSignature: '',
|
||||||
qrCodeData: '',
|
qrCodeData: '',
|
||||||
|
qrCodeImage: '',
|
||||||
|
mark: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 标签数据
|
|
||||||
// const labelData = reactive({
|
|
||||||
// partName: '物料3',
|
|
||||||
// partNumber: '1',
|
|
||||||
// totalCalculatedWeight: '100',
|
|
||||||
// totalWeight: '100',
|
|
||||||
// productionDate: '202401010000',
|
|
||||||
// totalCount: '100',
|
|
||||||
// packingSignature: '',
|
|
||||||
// inspectionSignature: '',
|
|
||||||
// qrCodeData: '10#$$DY',
|
|
||||||
// })
|
|
||||||
|
|
||||||
// 标签容器引用
|
// 标签容器引用
|
||||||
const labelContainer = ref<HTMLElement | null>(null)
|
const labelContainer = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
// 生成标签
|
// 生成二维码
|
||||||
const generateLabel = async () => {
|
const generateQRCode = async (data: string) => {
|
||||||
if (!formData.productionBatch) {
|
try {
|
||||||
Message.error('请输入生产批次')
|
return await QRCode.toDataURL(data, {
|
||||||
|
width: 120,
|
||||||
|
margin: 1,
|
||||||
|
color: {
|
||||||
|
dark: '#000000',
|
||||||
|
light: '#FFFFFF'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成明细标签
|
||||||
|
const generateDetailLabel = async () => {
|
||||||
|
if (!formData.workOrderInfos || formData.workOrderInfos.length === 0) {
|
||||||
|
Message.error('未获取到工单明细信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.batch) {
|
||||||
|
console.log("11111", formData.batch);
|
||||||
|
Message.error('未获取到批次信息')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!formData.materialName) {
|
if (!formData.materialName) {
|
||||||
@@ -175,6 +278,9 @@ const generateLabel = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 清空之前的标签数据
|
||||||
|
labelDataList.length = 0
|
||||||
|
|
||||||
// 格式化生产日期为 yyyyMMddHHmm 格式
|
// 格式化生产日期为 yyyyMMddHHmm 格式
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const formattedDate = now.getFullYear().toString() +
|
const formattedDate = now.getFullYear().toString() +
|
||||||
@@ -185,7 +291,72 @@ const generateLabel = async () => {
|
|||||||
|
|
||||||
const formattedDate2 = now.getFullYear().toString() +
|
const formattedDate2 = now.getFullYear().toString() +
|
||||||
String(now.getMonth() + 1).padStart(2, '0') +
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
String(now.getDate()).padStart(2, '0');
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
// 为每个工单明细生成一个标签
|
||||||
|
for (const workOrderInfo of formData.workOrderInfos) {
|
||||||
|
// 计算二维码数据
|
||||||
|
const orderNo = formData.orderNo + workOrderInfo.id;
|
||||||
|
const qrCodeData = `10#${formData.encoding}$11#9DP$12#${formData.batch}$17#${workOrderInfo.quantity}$20#${formattedDate2}$31#${orderNo}$DY`
|
||||||
|
|
||||||
|
// 生成二维码图片
|
||||||
|
const qrCodeImage = await generateQRCode(qrCodeData)
|
||||||
|
|
||||||
|
// 添加标签数据
|
||||||
|
labelDataList.push({
|
||||||
|
partName: formData.materialName || '',
|
||||||
|
partNumber: formData.encoding || '',
|
||||||
|
totalCalculatedWeight: workOrderInfo.calculatedWeight || '',
|
||||||
|
totalWeight: workOrderInfo.weight || '',
|
||||||
|
productionDate: formattedDate,
|
||||||
|
totalCount: workOrderInfo.quantity || '',
|
||||||
|
packingSignature: '',
|
||||||
|
inspectionSignature: '',
|
||||||
|
qrCodeData: qrCodeData,
|
||||||
|
qrCodeImage: qrCodeImage,
|
||||||
|
mark: workOrderInfo.mark || '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Message.success(`成功生成 ${labelDataList.length} 个明细标签`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成明细标签失败:', error)
|
||||||
|
Message.error('生成明细标签失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成整体标签
|
||||||
|
const generateOverallLabel = async () => {
|
||||||
|
if (!formData.batch) {
|
||||||
|
Message.error('未获取到批次信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.materialName) {
|
||||||
|
Message.error('未获取到物料信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 清空明细标签数据
|
||||||
|
labelDataList.length = 0
|
||||||
|
|
||||||
|
// 格式化生产日期为 yyyyMMddHHmm 格式
|
||||||
|
const now = new Date()
|
||||||
|
const formattedDate = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0') +
|
||||||
|
String(now.getHours()).padStart(2, '0') +
|
||||||
|
String(now.getMinutes()).padStart(2, '0')
|
||||||
|
|
||||||
|
const formattedDate2 = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
// 计算二维码数据
|
||||||
|
const qrCodeData = `10#${formData.encoding}$11#9DP$12#${formData.batch}$17#${formData.totalCount}$20#${formattedDate2}$31#${formData.orderNo}$DY`
|
||||||
|
|
||||||
|
// 生成二维码图片
|
||||||
|
const qrCodeImage = await generateQRCode(qrCodeData)
|
||||||
|
|
||||||
// 直接从 formData 中获取数据
|
// 直接从 formData 中获取数据
|
||||||
Object.assign(labelData, {
|
Object.assign(labelData, {
|
||||||
@@ -197,8 +368,9 @@ const generateLabel = async () => {
|
|||||||
totalCount: formData.totalCount || '',
|
totalCount: formData.totalCount || '',
|
||||||
packingSignature: '',
|
packingSignature: '',
|
||||||
inspectionSignature: '',
|
inspectionSignature: '',
|
||||||
//10#零件号$11#供应商代码$12#生产批次$17#数量$20#包装日期$31#唯一号$DY
|
qrCodeData: qrCodeData,
|
||||||
qrCodeData: `10#${formData.materialName}$11#9DP$12#${formData.productionBatch}$17#${formData.totalCount}$20#${formattedDate2}$31#${formData.orderNo}$DY`
|
qrCodeImage: qrCodeImage,
|
||||||
|
mark: formData.mark || '',
|
||||||
})
|
})
|
||||||
|
|
||||||
Message.success('标签生成成功')
|
Message.success('标签生成成功')
|
||||||
@@ -220,16 +392,16 @@ onMounted(() => {
|
|||||||
formData.encoding = res.data.encoding
|
formData.encoding = res.data.encoding
|
||||||
formData.materialName = res.data.materialName
|
formData.materialName = res.data.materialName
|
||||||
formData.orderNo = res.data.orderNo
|
formData.orderNo = res.data.orderNo
|
||||||
|
formData.batch = res.data.batch
|
||||||
formData.totalCalculatedWeight = res.data.totalCalculatedWeight
|
formData.totalCalculatedWeight = res.data.totalCalculatedWeight
|
||||||
formData.totalWeight = res.data.totalWeight
|
formData.totalWeight = res.data.totalWeight
|
||||||
formData.totalCount = res.data.totalCount
|
formData.totalCount = res.data.totalCount
|
||||||
|
formData.workOrderInfos = res.data.workOrderInfos
|
||||||
|
formData.mark = res.data.mark
|
||||||
} else {
|
} else {
|
||||||
Message.error('获取详情失败')
|
Message.error('获取详情失败')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 自动生成标签
|
|
||||||
generateLabel()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -240,7 +412,81 @@ const printLabel = async () => {
|
|||||||
// 创建打印窗口
|
// 创建打印窗口
|
||||||
const printWindow = window.open('', '_blank')
|
const printWindow = window.open('', '_blank')
|
||||||
if (printWindow) {
|
if (printWindow) {
|
||||||
// 直接构建打印内容,确保二维码图片正确生成
|
// 判断是打印明细标签还是整体标签
|
||||||
|
const isDetailLabels = labelDataList.length > 0
|
||||||
|
const labelsToPrint = isDetailLabels ? labelDataList : [labelData]
|
||||||
|
|
||||||
|
// 生成标签HTML
|
||||||
|
const labelsHTML = labelsToPrint.map(item => `
|
||||||
|
<div class="label">
|
||||||
|
<table class="label-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件名称</div>
|
||||||
|
<div class="label-value">${item.partName}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">生产日期</div>
|
||||||
|
<div class="label-value">${item.productionDate}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img src="${item.qrCodeImage}" alt="QR Code" />
|
||||||
|
<div class="mark-number">${item.mark || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件号</div>
|
||||||
|
<div class="label-value">${item.partNumber}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">数量</div>
|
||||||
|
<div class="label-value">${item.totalCount}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">标重(g)</div>
|
||||||
|
<div class="label-value">${item.totalCalculatedWeight}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">包装签字</div>
|
||||||
|
<div class="label-value">${item.packingSignature || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">实重(g)</div>
|
||||||
|
<div class="label-value">${item.totalWeight || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">检验签字</div>
|
||||||
|
<div class="label-value">${item.inspectionSignature || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`).join('')
|
||||||
|
|
||||||
|
// 构建打印内容
|
||||||
const printHTML = `
|
const printHTML = `
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
@@ -253,79 +499,16 @@ const printLabel = async () => {
|
|||||||
.label-cell { border: 1px solid #000; padding: 1mm; vertical-align: top; }
|
.label-cell { border: 1px solid #000; padding: 1mm; vertical-align: top; }
|
||||||
.qr-cell { width: 24mm; text-align: center; vertical-align: middle; border: 1px solid #000; }
|
.qr-cell { width: 24mm; text-align: center; vertical-align: middle; border: 1px solid #000; }
|
||||||
.label-row { display: flex; align-items: center; }
|
.label-row { display: flex; align-items: center; }
|
||||||
.label-field { font-size: 7pt; font-weight: bold; margin-right: 2mm; min-width: 25pt; }
|
.label-field { font-size: 8pt; font-weight: bold; margin-right: 2mm; min-width: 25pt; }
|
||||||
.label-value { font-size: 7pt; flex: 1; }
|
.label-value { font-size: 8pt; font-weight: bold; flex: 1; word-break: break-all; }
|
||||||
.qr-code img { width: 20mm; height: 20mm; margin: 1mm 0; }
|
.qr-code img { width: 20mm; height: 20mm; margin: 1mm 0; }
|
||||||
.serial-number { font-size: 7pt; margin-top: 1mm; }
|
.mark-number { font-size: 8pt; font-weight: bold; margin-top: 1mm; text-align: center; }
|
||||||
|
.serial-number { font-size: 8pt; font-weight: bold; margin-top: 1mm; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="label-container">
|
<div class="label-container">
|
||||||
<div class="label">
|
${labelsHTML}
|
||||||
<table class="label-table">
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">零件名称</div>
|
|
||||||
<div class="label-value">${labelData.partName}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">生产日期</div>
|
|
||||||
<div class="label-value">${labelData.productionDate}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="label-cell qr-cell" rowspan="4">
|
|
||||||
<div class="qr-code">
|
|
||||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=${encodeURIComponent(labelData.qrCodeData)}" alt="QR Code" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">零件号</div>
|
|
||||||
<div class="label-value">${labelData.partNumber}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">数量</div>
|
|
||||||
<div class="label-value">${labelData.totalCount}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">标重(kg)</div>
|
|
||||||
<div class="label-value">${labelData.totalCalculatedWeight}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">包装签字</div>
|
|
||||||
<div class="label-value">${labelData.packingSignature || ''}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">实重(kg)</div>
|
|
||||||
<div class="label-value">${labelData.totalWeight || ''}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="label-cell">
|
|
||||||
<div class="label-row">
|
|
||||||
<div class="label-field">检验签字</div>
|
|
||||||
<div class="label-value">${labelData.inspectionSignature || ''}</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -346,7 +529,7 @@ const printLabel = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineOptions({ name: 'BarcodePrint' })
|
defineOptions({ name: 'print' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -386,6 +569,7 @@ defineOptions({ name: 'BarcodePrint' })
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 标签预览 */
|
/* 标签预览 */
|
||||||
@@ -405,9 +589,9 @@ defineOptions({ name: 'BarcodePrint' })
|
|||||||
|
|
||||||
.label-container {
|
.label-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-direction: column;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +636,9 @@ defineOptions({ name: 'BarcodePrint' })
|
|||||||
|
|
||||||
.label-value {
|
.label-value {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code img {
|
.qr-code img {
|
||||||
@@ -461,6 +647,23 @@ defineOptions({ name: 'BarcodePrint' })
|
|||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.qr-code .loading {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-number {
|
||||||
|
font-size: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.serial-number {
|
.serial-number {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
|||||||
@@ -32,13 +32,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<NoticeDetailModal ref="NoticeDetailModalRef" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type DashboardNoticeResp, listDashboardNotice } from '@/apis'
|
import { type DashboardNoticeResp, listDashboardNotice } from '@/apis'
|
||||||
import { useDict } from '@/hooks/app'
|
import { useDict } from '@/hooks/app'
|
||||||
import NoticeDetailModal from '@/views/system/notice/NoticeDetailModal.vue'
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { notice_type } = useDict('notice_type')
|
const { notice_type } = useDict('notice_type')
|
||||||
@@ -56,11 +54,6 @@ const getDataList = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoticeDetailModalRef = ref<InstanceType<typeof NoticeDetailModal>>()
|
|
||||||
// 详情
|
|
||||||
const onDetail = (id: string) => {
|
|
||||||
NoticeDetailModalRef.value?.onDetail(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getDataList()
|
getDataList()
|
||||||
|
|||||||
426
src/views/fullClaim/FullWorkOrderAddModal.vue
Normal file
426
src/views/fullClaim/FullWorkOrderAddModal.vue
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="新增整箱领取记录"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 800 ? 800 : '90%'"
|
||||||
|
:style="{ height: '90vh', maxHeight: '900px' }"
|
||||||
|
draggable
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" v-model="form">
|
||||||
|
<a-row :gutter="22">
|
||||||
|
<a-col :span="22">
|
||||||
|
<a-form-item label="物料编码" label-col-flex="100px">
|
||||||
|
<a-input
|
||||||
|
ref="materialCodeInput"
|
||||||
|
v-model="form.inputMaterialCode"
|
||||||
|
placeholder="请点击此处确保光标闪烁,且输入法为英文状态,使用扫码枪扫描物料编码"
|
||||||
|
@keydown="handleKeyDown"
|
||||||
|
@input="handleMaterialCodeChange"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="22">
|
||||||
|
<a-col :span="11">
|
||||||
|
<a-form-item label="手动输入编码" label-col-flex="100px">
|
||||||
|
<a-input
|
||||||
|
ref="inputMaterialCode"
|
||||||
|
v-model="form.inputMaterialCode2"
|
||||||
|
placeholder="请输入物料编码"
|
||||||
|
@change="handleMaterialCodeChange2"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="11">
|
||||||
|
<a-form-item label="序号">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.mark"
|
||||||
|
:min="1"
|
||||||
|
placeholder="请输入序号"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="22">
|
||||||
|
<a-col :span="11">
|
||||||
|
<a-form-item label="物料名称" label-col-flex="100px">
|
||||||
|
<a-input
|
||||||
|
ref="materialName"
|
||||||
|
v-model="form.materialName"
|
||||||
|
placeholder="-"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="11">
|
||||||
|
<a-form-item label="批次">
|
||||||
|
<a-input
|
||||||
|
ref="batch"
|
||||||
|
v-model="form.batch"
|
||||||
|
placeholder="-"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="22">
|
||||||
|
<a-col :span="11">
|
||||||
|
<a-form-item label="数量" label-col-flex="100px">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.count"
|
||||||
|
placeholder="整箱数量"
|
||||||
|
:min="0"
|
||||||
|
:disabled = "disableCount"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="22">
|
||||||
|
<a-col :span="22">
|
||||||
|
<a-form-item label="图片" label-col-flex="100px">
|
||||||
|
<div class="image-container">
|
||||||
|
<img
|
||||||
|
:src="imgData.imgUrl"
|
||||||
|
alt="图片"
|
||||||
|
style="width: 100%; height: 100%; object-fit: cover; border-radius: 4px;"
|
||||||
|
/>
|
||||||
|
<!-- 错误状态 -->
|
||||||
|
<div v-if="weighingPageStatus === 'error'" class="video-overlay error">
|
||||||
|
<icon-close-circle-fill style="color: #ff4d4f; font-size: 24px;" />
|
||||||
|
<span>连接异常</span>
|
||||||
|
<Button size="small" type="primary" @click="enterWeighPage">重试</Button>
|
||||||
|
</div>
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="weighingPageStatus === 'entering'" class="video-overlay">
|
||||||
|
<Spin />
|
||||||
|
<span style="margin-left: 8px;">加载中...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
|
||||||
|
import {type FormInstance, Message, Spin, Button, Icon} from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import { addFullWorkOrder } from '@/apis/fullWorkOrder/fullWorkOrder'
|
||||||
|
import {getCaptureImage, getEnterWeighPage, getLeaveWeighPage} from '@/apis/weightManage/ys'
|
||||||
|
import { useResetReactive } from '@/hooks'
|
||||||
|
import {getMaterialDetail} from "@/apis/weightManage/weightManage";
|
||||||
|
import {number} from "echarts";
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { width, height } = useWindowSize()
|
||||||
|
|
||||||
|
const dataId = ref('')
|
||||||
|
const visible = ref(false)
|
||||||
|
const disableCount = ref(false)
|
||||||
|
const materialCodeInput = ref<any>(null)
|
||||||
|
|
||||||
|
// 称重页面状态
|
||||||
|
const weighingPageStatus = ref<'idle' | 'entering' | 'entered' | 'error'>('idle')
|
||||||
|
|
||||||
|
const [form, resetForm] = useResetReactive({
|
||||||
|
materialCode: '',
|
||||||
|
imgUrl: '',
|
||||||
|
materialName: '',
|
||||||
|
batch: '',
|
||||||
|
count: undefined,
|
||||||
|
inputMaterialCode: '',
|
||||||
|
inputMaterialCode2: '',
|
||||||
|
mark: undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const imgData = reactive({
|
||||||
|
imgUrl: 'http://localhost:6609/file/ys/carousel.jpg', // 称重页面图片URL
|
||||||
|
baseUrl: 'http://localhost:6609/file/ys/carousel.jpg' // 基础URL
|
||||||
|
})
|
||||||
|
|
||||||
|
// 图片刷新定时器
|
||||||
|
let imageRefreshTimer: any = null
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防抖函数
|
||||||
|
const debounce = <T extends (...args: any[]) => any>(func: T, delay: number): ((...args: Parameters<T>) => void) => {
|
||||||
|
let timer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
return function (...args: Parameters<T>) {
|
||||||
|
if (timer) clearTimeout(timer)
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
func(...args)
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原始的物料编码变化处理函数
|
||||||
|
const originalHandleMaterialCodeChange = async () => {
|
||||||
|
// 确保输入框内容是完整的物料编码
|
||||||
|
// todo
|
||||||
|
const materialCode = form.inputMaterialCode?.trim()
|
||||||
|
// const materialCode = "831002839562,1,0.12,KP,0A2005,0A200520260325";
|
||||||
|
|
||||||
|
// 无论是否有输入,先重置所有物料相关字段,确保新数据能完全覆盖旧数据
|
||||||
|
form.materialCode = ''
|
||||||
|
form.materialName = ''
|
||||||
|
form.inputMaterialCode2 = ''
|
||||||
|
form.batch = ''
|
||||||
|
form.count = undefined
|
||||||
|
form.mark = undefined
|
||||||
|
|
||||||
|
// 如果有物料编码输入,获取物料数据
|
||||||
|
if (materialCode) {
|
||||||
|
try {
|
||||||
|
disableCount.value = true
|
||||||
|
const parts = materialCode.split(',');
|
||||||
|
form.count = parseFloat(parts[2]) * 1000;
|
||||||
|
console.log("form.count", form.count);
|
||||||
|
await fetchMaterialData(materialCode)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取物料数据失败:', error)
|
||||||
|
// 即使获取失败,也保持字段为空,避免显示旧数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带防抖的物料编码变化处理函数
|
||||||
|
const handleMaterialCodeChange = debounce(originalHandleMaterialCodeChange, 500) // 500ms防抖延迟
|
||||||
|
|
||||||
|
// 扫码核验获取物料信息
|
||||||
|
const fetchMaterialData = async (code: string) => {
|
||||||
|
const res = await getMaterialDetail(code);
|
||||||
|
if (res.code === '0') {
|
||||||
|
// 更新表单数据
|
||||||
|
form.materialCode = res.data?.encoding || ''
|
||||||
|
form.materialName = res.data?.materialName || ''
|
||||||
|
form.batch = res.data?.batch || ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMaterialCodeChange2 = async (code: string) => {
|
||||||
|
if (!code || code?.trim()=== '') {
|
||||||
|
form.materialCode = ''
|
||||||
|
form.materialName = ''
|
||||||
|
form.batch = ''
|
||||||
|
form.mark = undefined
|
||||||
|
form.count = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
disableCount.value = false
|
||||||
|
form.inputMaterialCode = ''
|
||||||
|
form.mark = undefined
|
||||||
|
form.count = undefined
|
||||||
|
const res = await getMaterialDetail(code);
|
||||||
|
if (res.code === '0') {
|
||||||
|
// 更新表单数据
|
||||||
|
form.materialCode = res.data?.encoding || ''
|
||||||
|
form.materialName = res.data?.materialName || ''
|
||||||
|
form.batch = res.data?.batch || ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
const save = async () => {
|
||||||
|
if (!form.materialCode || form.materialCode.trim() === '') {
|
||||||
|
Message.error('未找到物料信息');
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!form.count) {
|
||||||
|
Message.error('数量不能为空');
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!form.batch || form.batch.trim() === '') {
|
||||||
|
Message.error('未找到批次');
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!form.mark) {
|
||||||
|
Message.error('请输入序号');
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
//手动抓图
|
||||||
|
const data = {
|
||||||
|
type: 2,
|
||||||
|
}
|
||||||
|
// todo
|
||||||
|
const response = await getCaptureImage(data);
|
||||||
|
// const response = {'data': 'http://localhost:6609/file/ys/carousel.jpg'};
|
||||||
|
if (response) {
|
||||||
|
form.imgUrl = response.data;
|
||||||
|
} else {
|
||||||
|
Message.error('抓图失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
await addFullWorkOrder(form)
|
||||||
|
Message.success('新增成功')
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动宇视SDK
|
||||||
|
const enterWeighPage = async () => {
|
||||||
|
weighingPageStatus.value = 'entering'
|
||||||
|
try {
|
||||||
|
await getEnterWeighPage()
|
||||||
|
weighingPageStatus.value = 'entered'
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动宇视SDK失败:', error)
|
||||||
|
weighingPageStatus.value = 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 退出宇视SDK
|
||||||
|
const leaveWeighPage = async () => {
|
||||||
|
try {
|
||||||
|
await getLeaveWeighPage()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('退出宇视SDK失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const onAdd = async () => {
|
||||||
|
reset()
|
||||||
|
dataId.value = ''
|
||||||
|
visible.value = true
|
||||||
|
// 启动宇视SDK
|
||||||
|
await enterWeighPage()
|
||||||
|
// 聚焦到物料编码输入框
|
||||||
|
nextTick(() => {
|
||||||
|
if (materialCodeInput.value) {
|
||||||
|
materialCodeInput.value.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录上次输入时间,用于判断是否是扫码枪输入
|
||||||
|
let lastInputTime = 0
|
||||||
|
// 记录是否正在接收扫码输入
|
||||||
|
let isScanning = false
|
||||||
|
|
||||||
|
// 处理键盘按下事件
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
// 获取当前时间
|
||||||
|
const currentTime = Date.now()
|
||||||
|
|
||||||
|
// 计算时间差
|
||||||
|
const timeDiff = currentTime - lastInputTime
|
||||||
|
// 检查是否是回车键(扫码枪通常以回车键结束)
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
// 扫码结束,标记为不在扫描中
|
||||||
|
isScanning = false
|
||||||
|
// 不阻止默认行为,让表单可以正常提交
|
||||||
|
}
|
||||||
|
// 检查是否是新的扫码开始
|
||||||
|
// 当时间间隔大于300ms,且不是修饰键,且不是功能键时,认为是新的扫码开始
|
||||||
|
else if (timeDiff > 300 && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
||||||
|
form.inputMaterialCode = ''
|
||||||
|
// 标记为开始扫描
|
||||||
|
isScanning = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新上次输入时间
|
||||||
|
lastInputTime = currentTime
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始时不启动图片刷新
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件卸载时
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// 清除图片刷新定时器
|
||||||
|
if (imageRefreshTimer) {
|
||||||
|
clearInterval(imageRefreshTimer)
|
||||||
|
imageRefreshTimer = null
|
||||||
|
}
|
||||||
|
// 退出宇视SDK
|
||||||
|
leaveWeighPage()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听visible变化
|
||||||
|
watch(visible, async (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
// 当弹窗打开时,启动图片自动刷新,每1.5秒更新一次
|
||||||
|
imageRefreshTimer = setInterval(() => {
|
||||||
|
// 添加时间戳参数,避免浏览器缓存
|
||||||
|
imgData.imgUrl = `${imgData.baseUrl}?t=${Date.now()}`
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
// 当弹窗关闭时,退出宇视SDK并停止图片刷新
|
||||||
|
await leaveWeighPage()
|
||||||
|
if (imageRefreshTimer) {
|
||||||
|
clearInterval(imageRefreshTimer)
|
||||||
|
imageRefreshTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ onAdd })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.image-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 视频覆盖层样式(用于错误和加载状态) */
|
||||||
|
.video-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-overlay.error {
|
||||||
|
background: rgba(255, 77, 79, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用输入框样式 - 黑色加粗字体 */
|
||||||
|
:deep(.arco-input-wrapper.arco-input-disabled) {
|
||||||
|
background-color: #ffffff !important;
|
||||||
|
border-color: #d9d9d9 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-input-wrapper.arco-input-disabled .arco-input) {
|
||||||
|
color: #000000 !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
-webkit-text-fill-color: #000000 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
69
src/views/fullClaim/FullWorkOrderDetailListModal.vue
Normal file
69
src/views/fullClaim/FullWorkOrderDetailListModal.vue
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="原材料详情"
|
||||||
|
:width="800"
|
||||||
|
:footer="false"
|
||||||
|
@cancel="handleClose"
|
||||||
|
>
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="detailList"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="false"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #imgUrl="{ record }">
|
||||||
|
<a-image
|
||||||
|
width="80"
|
||||||
|
height="60"
|
||||||
|
:src="record.imgUrl"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { getFullWorkOrderDetailList } from '@/apis/fullWorkOrder/fullWorkOrder'
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const detailList = ref<any[]>([])
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: '称重重量(g)', dataIndex: 'weight', key: 'weight' },
|
||||||
|
{ title: '截图', dataIndex: 'imgUrl', key: 'imgUrl', slotName: 'imgUrl' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const onOpen = async (id: string) => {
|
||||||
|
visible.value = true
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getFullWorkOrderDetailList(id)
|
||||||
|
if (res.code === '0') {
|
||||||
|
detailList.value = res.data || []
|
||||||
|
} else {
|
||||||
|
Message.error(res.msg || '获取详情失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取详情失败:', error)
|
||||||
|
Message.error('获取详情失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
detailList.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onOpen })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
368
src/views/fullClaim/FullWorkOrderDetailModal.vue
Normal file
368
src/views/fullClaim/FullWorkOrderDetailModal.vue
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="新增原材料详情"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="1200"
|
||||||
|
:style="{ height: '80vh' }"
|
||||||
|
draggable
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<div class="detail-container">
|
||||||
|
<div class="left-section">
|
||||||
|
<div class="weight-input">
|
||||||
|
<label>称重重量(g)</label>
|
||||||
|
<a-input v-model="weightValue" placeholder="等待电子秤数据..." disabled />
|
||||||
|
</div>
|
||||||
|
<div class="camera-section">
|
||||||
|
<div class="image-container">
|
||||||
|
<img
|
||||||
|
:src="imgData.imgUrl"
|
||||||
|
alt="宇视摄像头实时画面"
|
||||||
|
style="width: 100%; height: 100%; object-fit: cover; border-radius: 4px;"
|
||||||
|
/>
|
||||||
|
<div v-if="cameraStatus === 'error'" class="video-overlay error">
|
||||||
|
<icon-close-circle-fill style="color: #ff4d4f; font-size: 24px;" />
|
||||||
|
<span>连接异常</span>
|
||||||
|
<a-button size="small" type="primary" @click="enterCamera">重试</a-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="cameraStatus === 'entering'" class="video-overlay">
|
||||||
|
<a-spin />
|
||||||
|
<span style="margin-left: 8px;">加载中...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="confirm-button">
|
||||||
|
<a-button type="primary" @click="handleConfirm">确定</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-section">
|
||||||
|
<div class="detail-list">
|
||||||
|
<a-table :columns="detailColumns" :data="detailList" :pagination="false" bordered>
|
||||||
|
<template #imgUrl="{ record }">
|
||||||
|
<a-image width="80" height="60" :src="record.imgUrl" />
|
||||||
|
</template>
|
||||||
|
<template #action="{ record }">
|
||||||
|
<a-button type="text" status="danger" @click="handleDeleteDetail(record)">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted, onBeforeUnmount, nextTick, watch } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { getCaptureImage, getEnterWeighPage, getLeaveWeighPage } from '@/apis/weightManage/ys'
|
||||||
|
import { weighAHStart, weighAHStop } from '@/apis/weightManage/weightManage'
|
||||||
|
import { saveFullWorkOrderDetail } from '@/apis/fullWorkOrder/fullWorkOrder'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const weightValue = ref('')
|
||||||
|
const cameraStatus = ref<'idle' | 'entering' | 'entered' | 'error'>('idle')
|
||||||
|
const fullWorkOrder = ref('')
|
||||||
|
|
||||||
|
const imgData = reactive({
|
||||||
|
imgUrl: 'http://localhost:6609/file/ys/carousel.jpg',
|
||||||
|
baseUrl: 'http://localhost:6609/file/ys/carousel.jpg'
|
||||||
|
})
|
||||||
|
|
||||||
|
let imageRefreshTimer: any = null
|
||||||
|
|
||||||
|
const detailList = ref<any[]>([])
|
||||||
|
|
||||||
|
const detailColumns = [
|
||||||
|
{ title: '称重重量(g)', dataIndex: 'weight', key: 'weight' },
|
||||||
|
{ title: '截图', dataIndex: 'imgUrl', key: 'imgUrl', slotName: 'imgUrl' },
|
||||||
|
{ title: '操作', dataIndex: 'action', key: 'action', slotName: 'action', width: 80 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// WebSocket连接
|
||||||
|
const ws = ref<WebSocket | null>(null)
|
||||||
|
const wsConnected = ref(false)
|
||||||
|
|
||||||
|
// 建立WebSocket连接(电子称)
|
||||||
|
const establishWebSocket = () => {
|
||||||
|
try {
|
||||||
|
const wsUrl = 'ws://localhost:6609/ws/scale'
|
||||||
|
ws.value = new WebSocket(wsUrl)
|
||||||
|
|
||||||
|
ws.value.onopen = () => {
|
||||||
|
wsConnected.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.value.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
if (event.data) {
|
||||||
|
weightValue.value = event.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WebSocket消息解析失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.value.onclose = () => {
|
||||||
|
wsConnected.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.value.onerror = (error) => {
|
||||||
|
console.error('WebSocket错误:', error)
|
||||||
|
wsConnected.value = false
|
||||||
|
Message.error('称重连接失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('建立WebSocket连接失败:', error)
|
||||||
|
Message.error('无法建立称重连接')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭WebSocket连接(电子称)
|
||||||
|
const closeWebSocket = () => {
|
||||||
|
if (ws.value) {
|
||||||
|
ws.value.close()
|
||||||
|
ws.value = null
|
||||||
|
wsConnected.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接电子称
|
||||||
|
const connectScale = async () => {
|
||||||
|
try {
|
||||||
|
await weighAHStart()
|
||||||
|
establishWebSocket()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('连接电子称失败:', error)
|
||||||
|
Message.error('与电子称的连接建立失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 断开电子称
|
||||||
|
const disconnectScale = async () => {
|
||||||
|
try {
|
||||||
|
await weighAHStop()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('断开电子称失败:', error)
|
||||||
|
}
|
||||||
|
closeWebSocket()
|
||||||
|
}
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
weightValue.value = ''
|
||||||
|
detailList.value = []
|
||||||
|
fullWorkOrder.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const enterCamera = async () => {
|
||||||
|
cameraStatus.value = 'entering'
|
||||||
|
try {
|
||||||
|
await getEnterWeighPage()
|
||||||
|
cameraStatus.value = 'entered'
|
||||||
|
} catch (error) {
|
||||||
|
console.error('进入摄像头页面失败:', error)
|
||||||
|
cameraStatus.value = 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const leaveCamera = async () => {
|
||||||
|
try {
|
||||||
|
await getLeaveWeighPage()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('离开摄像头页面失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAdd = async (id: string) => {
|
||||||
|
reset()
|
||||||
|
fullWorkOrder.value = id
|
||||||
|
visible.value = true
|
||||||
|
// 独立连接电子称和摄像头
|
||||||
|
await Promise.all([
|
||||||
|
enterCamera(),
|
||||||
|
connectScale()
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConfirm = async () => {
|
||||||
|
if (!weightValue.value || weightValue.value.trim() === '') {
|
||||||
|
Message.error('电子秤称重结果为空!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = {
|
||||||
|
type: 2,
|
||||||
|
}
|
||||||
|
// todo
|
||||||
|
const response = await getCaptureImage(data)
|
||||||
|
// const response = {
|
||||||
|
// data: 'http://localhost:6609/file/ys/workOrder/fullOrder_1774322914630.jpg',
|
||||||
|
// }
|
||||||
|
if (response && response.data) {
|
||||||
|
const newItem = {
|
||||||
|
key: Date.now().toString(),
|
||||||
|
weight: weightValue.value,
|
||||||
|
imgUrl: response.data
|
||||||
|
}
|
||||||
|
detailList.value.push(newItem)
|
||||||
|
weightValue.value = ''
|
||||||
|
Message.success('添加成功')
|
||||||
|
} else {
|
||||||
|
Message.error('抓图失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('抓图失败:', error)
|
||||||
|
Message.error('抓图失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDeleteDetail = (record: any) => {
|
||||||
|
detailList.value = detailList.value.filter(item => item.key !== record.key)
|
||||||
|
Message.success('删除成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
if (detailList.value.length === 0) {
|
||||||
|
Message.error('请至少添加一条详情记录')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = detailList.value.map(item => ({
|
||||||
|
weight: item.weight,
|
||||||
|
imgUrl: item.imgUrl,
|
||||||
|
fullWorkOrderId: fullWorkOrder.value,
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
const res = await saveFullWorkOrderDetail(data)
|
||||||
|
if (res.code === '0') {
|
||||||
|
Message.success('保存成功')
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
Message.error(res.msg || '保存失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存失败:', error)
|
||||||
|
Message.error('保存失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (imageRefreshTimer) {
|
||||||
|
clearInterval(imageRefreshTimer)
|
||||||
|
imageRefreshTimer = null
|
||||||
|
}
|
||||||
|
leaveCamera()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(visible, async (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
imageRefreshTimer = setInterval(() => {
|
||||||
|
imgData.imgUrl = `${imgData.baseUrl}?t=${Date.now()}`
|
||||||
|
}, 1500)
|
||||||
|
} else {
|
||||||
|
// 独立断开电子称和摄像头
|
||||||
|
await Promise.all([
|
||||||
|
leaveCamera(),
|
||||||
|
disconnectScale()
|
||||||
|
])
|
||||||
|
if (imageRefreshTimer) {
|
||||||
|
clearInterval(imageRefreshTimer)
|
||||||
|
imageRefreshTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ onAdd })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.detail-container {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-section {
|
||||||
|
width: 45%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weight-input {
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-section {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-overlay.error {
|
||||||
|
background: rgba(255, 77, 79, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-section {
|
||||||
|
width: 55%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
349
src/views/fullClaim/index.vue
Normal file
349
src/views/fullClaim/index.vue
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gi_table_page">
|
||||||
|
<GiTable
|
||||||
|
title="整箱领取记录管理"
|
||||||
|
row-key="id"
|
||||||
|
:data="dataList"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
|
:pagination="pagination"
|
||||||
|
:disabled-tools="['size']"
|
||||||
|
:disabled-column-keys="['name']"
|
||||||
|
@refresh="search"
|
||||||
|
>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<a-input-search v-model="queryForm.orderNo" placeholder="请输入任务工单号" allow-clear @search="search" />
|
||||||
|
<a-input-search v-model="queryForm.materialCode" placeholder="请输入物料编码" allow-clear @search="search" />
|
||||||
|
<a-input-search v-model="queryForm.materialName" placeholder="请输入物料名称" allow-clear @search="search" />
|
||||||
|
<a-input-search v-model="queryForm.batch" placeholder="请输入批次号" allow-clear @search="search" />
|
||||||
|
<a-range-picker
|
||||||
|
v-model="queryForm.createTime"
|
||||||
|
:show-time="true"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
style="height: 32px"
|
||||||
|
:allow-clear="true"
|
||||||
|
@change="search"
|
||||||
|
/>
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
<template #default>重置</template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #toolbar-right>
|
||||||
|
<!-- <a-button v-permission="['fullWorkOrder:fullWorkOrder:add']" type="primary" @click="onAdd">-->
|
||||||
|
<!-- <template #icon><icon-plus /></template>-->
|
||||||
|
<!-- <template #default>新增</template>-->
|
||||||
|
<!-- </a-button>-->
|
||||||
|
<a-button v-permission="['fullWorkOrder:fullWorkOrder:export']" @click="onExport">
|
||||||
|
<template #icon><icon-download /></template>
|
||||||
|
<template #default>导出</template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #imgUrl="{ record }">
|
||||||
|
<a-image
|
||||||
|
width="60"
|
||||||
|
:src="record.imgUrl"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #action="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<!-- <a-link-->
|
||||||
|
<!-- @click="onAddDetail(record.id)"-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- 新增-->
|
||||||
|
<!-- </a-link>-->
|
||||||
|
<a-link
|
||||||
|
@click="onViewDetail(record)"
|
||||||
|
>
|
||||||
|
详情
|
||||||
|
</a-link>
|
||||||
|
<a-link
|
||||||
|
@click="onPrint(record)"
|
||||||
|
>
|
||||||
|
打印
|
||||||
|
</a-link>
|
||||||
|
<a-link
|
||||||
|
v-permission="['fullWorkOrder:fullWorkOrder:delete']"
|
||||||
|
status="danger"
|
||||||
|
:disabled="record.disabled"
|
||||||
|
:title="record.disabled ? '不可删除' : '删除'"
|
||||||
|
@click="onDelete(record)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</a-link>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</GiTable>
|
||||||
|
|
||||||
|
<!-- <FullWorkOrderAddModal ref="FullWorkOrderAddModalRef" @save-success="search" />-->
|
||||||
|
<!-- <FullWorkOrderDetailModal ref="FullWorkOrderDetailModalRef" @save-success="search" />-->
|
||||||
|
<FullWorkOrderDetailListModal ref="FullWorkOrderDetailListModalRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// import FullWorkOrderAddModal from './FullWorkOrderAddModal.vue'
|
||||||
|
// import FullWorkOrderDetailModal from './FullWorkOrderDetailModal.vue'
|
||||||
|
import FullWorkOrderDetailListModal from './FullWorkOrderDetailListModal.vue'
|
||||||
|
import { type FullWorkOrderResp, type FullWorkOrderQuery, deleteFullWorkOrder, exportFullWorkOrder, listFullWorkOrder } from '@/apis/fullWorkOrder/fullWorkOrder'
|
||||||
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
|
import { useDownload, useTable } from '@/hooks'
|
||||||
|
import { isMobile } from '@/utils'
|
||||||
|
import has from '@/utils/has'
|
||||||
|
import QRCode from 'qrcode';
|
||||||
|
import {Message} from "@arco-design/web-vue";
|
||||||
|
|
||||||
|
defineOptions({ name: 'FullWorkOrder' })
|
||||||
|
|
||||||
|
|
||||||
|
const queryForm = reactive<FullWorkOrderQuery>({
|
||||||
|
orderNo: undefined,
|
||||||
|
materialCode: undefined,
|
||||||
|
materialName: undefined,
|
||||||
|
batch: undefined,
|
||||||
|
createTime: undefined,
|
||||||
|
sort: ['f.id,desc']
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
tableData: dataList,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
search,
|
||||||
|
handleDelete
|
||||||
|
} = useTable((page) => listFullWorkOrder({ ...queryForm, ...page }), { immediate: true })
|
||||||
|
const columns = ref<TableInstanceColumns[]>([
|
||||||
|
{ title: '标题', dataIndex: 'title', slotName: 'title' },
|
||||||
|
{ title: '任务工单号', dataIndex: 'orderNo', slotName: 'orderNo' },
|
||||||
|
{ title: '物料名称', dataIndex: 'materialName', slotName: 'materialName' },
|
||||||
|
{ title: '物料编码', dataIndex: 'materialCode', slotName: 'materialCode' },
|
||||||
|
{ title: '批次号', dataIndex: 'batch', slotName: 'batch' },
|
||||||
|
{ title: '数量', dataIndex: 'count', slotName: 'count' },
|
||||||
|
{ title: '标记号', dataIndex: 'mark', slotName: 'mark' },
|
||||||
|
{ title: '抓拍图', dataIndex: 'imgUrl', slotName: 'imgUrl' },
|
||||||
|
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime' },
|
||||||
|
{ title: '修改人', dataIndex: 'updateUserString', slotName: 'updateUser', show: false },
|
||||||
|
{ title: '修改时间', dataIndex: 'updateTime', slotName: 'updateTime', show: false },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
slotName: 'action',
|
||||||
|
width: 220,
|
||||||
|
align: 'center',
|
||||||
|
fixed: !isMobile() ? 'right' : undefined,
|
||||||
|
show: has.hasPermOr(['fullWorkOrder:fullWorkOrder:detail', 'fullWorkOrder:fullWorkOrder:update', 'fullWorkOrder:fullWorkOrder:delete'])
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
queryForm.orderNo = undefined
|
||||||
|
queryForm.materialCode = undefined
|
||||||
|
queryForm.materialName = undefined
|
||||||
|
queryForm.batch = undefined
|
||||||
|
queryForm.createTime = undefined
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const onDelete = (record: FullWorkOrderResp) => {
|
||||||
|
return handleDelete(() => deleteFullWorkOrder(record.id), {
|
||||||
|
content: `是否确定删除该条数据?`,
|
||||||
|
showModal: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成二维码
|
||||||
|
const generateQRCode = async (data: string) => {
|
||||||
|
try {
|
||||||
|
return await QRCode.toDataURL(data, {
|
||||||
|
width: 120,
|
||||||
|
margin: 1,
|
||||||
|
color: {
|
||||||
|
dark: '#000000',
|
||||||
|
light: '#FFFFFF'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPrint = async (record: FullWorkOrderResp) => {
|
||||||
|
if (!record.batch || record.batch.trim() === '') {
|
||||||
|
Message.error('该条记录批次号为空,无法打印!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!record.mark || record.mark.trim() === '') {
|
||||||
|
Message.error('该条记录标记号为空,无法打印!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!record.count) {
|
||||||
|
Message.error('该条记录数量为空,无法打印!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 格式化生产日期为 yyyyMMddHHmm 格式
|
||||||
|
const now = new Date()
|
||||||
|
const formattedDate = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0') +
|
||||||
|
String(now.getHours()).padStart(2, '0') +
|
||||||
|
String(now.getMinutes()).padStart(2, '0')
|
||||||
|
|
||||||
|
const formattedDate2 = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
// 计算二维码数据
|
||||||
|
const qrCodeData = `10#${record.materialCode || ''}$11#9DP$12#${record.batch || ''}$17#${record.count || ''}$20#${formattedDate2}$31#${record.orderNo || ''}$DY`
|
||||||
|
|
||||||
|
// 生成二维码图片
|
||||||
|
const qrCodeImage = await generateQRCode(qrCodeData)
|
||||||
|
|
||||||
|
// 直接生成打印标签
|
||||||
|
const printWindow = window.open('', '_blank');
|
||||||
|
if (!printWindow) return;
|
||||||
|
|
||||||
|
// 构建打印内容
|
||||||
|
const printContent = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>标签打印</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; padding: 10mm; font-family: Arial, sans-serif; }
|
||||||
|
.label-container { display: flex; flex-wrap: wrap; gap: 10mm; justify-content: center; }
|
||||||
|
.label { width: 90mm; height: 38.5mm; border: 1px solid #000; padding: 0; box-sizing: border-box; page-break-inside: avoid; }
|
||||||
|
.label-table { width: 100%; height: 100%; border-collapse: collapse; }
|
||||||
|
.label-cell { border: 1px solid #000; padding: 1mm; vertical-align: top; }
|
||||||
|
.qr-cell { width: 24mm; text-align: center; vertical-align: middle; border: 1px solid #000; }
|
||||||
|
.label-row { display: flex; align-items: center; }
|
||||||
|
.label-field { font-size: 8pt; font-weight: bold; margin-right: 2mm; min-width: 25pt; }
|
||||||
|
.label-value { font-size: 8pt; font-weight: bold; flex: 1; word-break: break-all; }
|
||||||
|
.qr-code img { width: 20mm; height: 20mm; margin: 1mm 0; }
|
||||||
|
.mark-number { font-size: 8pt; font-weight: bold; margin-top: 1mm; text-align: center; }
|
||||||
|
.serial-number { font-size: 8pt; font-weight: bold; margin-top: 1mm; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="label-container">
|
||||||
|
<div class="label">
|
||||||
|
<table class="label-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件名称</div>
|
||||||
|
<div class="label-value">${record.materialName || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">生产日期</div>
|
||||||
|
<div class="label-value">${formattedDate}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img src="${qrCodeImage}" alt="QR Code" />
|
||||||
|
<div class="mark-number">${record.mark || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件号</div>
|
||||||
|
<div class="label-value">${record.materialCode || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">数量</div>
|
||||||
|
<div class="label-value">${record.count || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">标重(g)</div>
|
||||||
|
<div class="label-value"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">包装签字</div>
|
||||||
|
<div class="label-value"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">实重(g)</div>
|
||||||
|
<div class="label-value"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">检验签字</div>
|
||||||
|
<div class="label-value"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
printWindow.document.write(printContent);
|
||||||
|
printWindow.document.close();
|
||||||
|
|
||||||
|
// 等待页面加载完成后打印
|
||||||
|
printWindow.onload = function() {
|
||||||
|
setTimeout(() => {
|
||||||
|
printWindow.focus();
|
||||||
|
printWindow.print();
|
||||||
|
printWindow.close();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('打印标签失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const onExport = () => {
|
||||||
|
useDownload(() => exportFullWorkOrder(queryForm))
|
||||||
|
}
|
||||||
|
|
||||||
|
// const FullWorkOrderAddModalRef = ref<InstanceType<typeof FullWorkOrderAddModal>>()
|
||||||
|
// const FullWorkOrderDetailModalRef = ref<InstanceType<typeof FullWorkOrderDetailModal>>()
|
||||||
|
const FullWorkOrderDetailListModalRef = ref<InstanceType<typeof FullWorkOrderDetailListModal>>()
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
// const onAdd = () => {
|
||||||
|
// FullWorkOrderAddModalRef.value?.onAdd()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 新增原材料详情
|
||||||
|
// const onAddDetail = (id: string) => {
|
||||||
|
// FullWorkOrderDetailModalRef.value?.onAdd(id)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 查看原材料详情
|
||||||
|
const onViewDetail = (record: FullWorkOrderResp) => {
|
||||||
|
FullWorkOrderDetailListModalRef.value?.onOpen(record.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
@submit="handleLogin"
|
@submit="handleLogin"
|
||||||
>
|
>
|
||||||
<a-form-item field="username" hide-label>
|
<a-form-item field="username" hide-label>
|
||||||
<a-input v-model="form.username" placeholder="请输入用户名" allow-clear />
|
<a-input v-model="form.username" placeholder="请输入用户名" allow-clear autocomplete="off" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="password" hide-label>
|
<a-form-item field="password" hide-label>
|
||||||
<a-input-password v-model="form.password" placeholder="请输入密码" />
|
<a-input-password v-model="form.password" placeholder="请输入密码" autocomplete="new-password" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="isCaptchaEnabled" field="captcha" hide-label>
|
<a-form-item v-if="isCaptchaEnabled" field="captcha" hide-label>
|
||||||
<a-input v-model="form.captcha" placeholder="请输入验证码" :max-length="4" allow-clear style="flex: 1 1" />
|
<a-input v-model="form.captcha" placeholder="请输入验证码" :max-length="4" allow-clear style="flex: 1 1" />
|
||||||
@@ -41,15 +41,16 @@
|
|||||||
import { type FormInstance, Message } from '@arco-design/web-vue'
|
import { type FormInstance, Message } from '@arco-design/web-vue'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { getImageCaptcha } from '@/apis/common'
|
import { getImageCaptcha } from '@/apis/common'
|
||||||
import { useTabsStore, useUserStore } from '@/stores'
|
import { useAppStore, useTabsStore, useUserStore } from '@/stores'
|
||||||
import { encryptByRsa } from '@/utils/encrypt'
|
import { encryptByRsa } from '@/utils/encrypt'
|
||||||
|
|
||||||
const loginConfig = useStorage('login-config', {
|
const loginConfig = useStorage('login-config', {
|
||||||
rememberMe: true,
|
// rememberMe: true,
|
||||||
username: 'admin', // 演示默认值
|
// username: 'admin', // 演示默认值
|
||||||
password: 'admin123', // 演示默认值
|
// password: 'admin123', // 演示默认值
|
||||||
// username: debug ? 'admin' : '', // 演示默认值
|
rememberMe: false,
|
||||||
// password: debug ? 'admin123' : '', // 演示默认值
|
username: '',
|
||||||
|
password: '',
|
||||||
})
|
})
|
||||||
// 是否启用验证码
|
// 是否启用验证码
|
||||||
const isCaptchaEnabled = ref(true)
|
const isCaptchaEnabled = ref(true)
|
||||||
@@ -106,6 +107,7 @@ const getCaptcha = () => {
|
|||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const tabsStore = useTabsStore()
|
const tabsStore = useTabsStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
// 登录
|
// 登录
|
||||||
@@ -120,6 +122,8 @@ const handleLogin = async () => {
|
|||||||
captcha: form.captcha,
|
captcha: form.captcha,
|
||||||
uuid: form.uuid,
|
uuid: form.uuid,
|
||||||
})
|
})
|
||||||
|
// 登录成功后获取系统配置(根据登录账号显示不同的系统名称)
|
||||||
|
await appStore.initSiteConfig()
|
||||||
tabsStore.reset()
|
tabsStore.reset()
|
||||||
const { redirect, ...othersQuery } = router.currentRoute.value.query
|
const { redirect, ...othersQuery } = router.currentRoute.value.query
|
||||||
const { rememberMe } = loginConfig.value
|
const { rememberMe } = loginConfig.value
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||||
import { type FormInstance, Message } from '@arco-design/web-vue'
|
import { type FormInstance, Message } from '@arco-design/web-vue'
|
||||||
import { useTabsStore, useUserStore } from '@/stores'
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useAppStore, useTabsStore, useUserStore } from '@/stores'
|
||||||
|
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -37,23 +38,30 @@ const rules: FormInstance['rules'] = {
|
|||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const tabsStore = useTabsStore()
|
const tabsStore = useTabsStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
// 登录
|
// 登录
|
||||||
const handleLogin = async () => {
|
const handleLogin = async () => {
|
||||||
|
console.log("卡号登录handleLogin")
|
||||||
// 检查用户是否已经登录
|
// 检查用户是否已经登录
|
||||||
if (userStore.token) {
|
if (userStore.token) {
|
||||||
|
console.log("卡号-用户已登录")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInvalid = await formRef.value?.validate()
|
const isInvalid = await formRef.value?.validate()
|
||||||
if (isInvalid) return
|
if (isInvalid) return
|
||||||
try {
|
try {
|
||||||
|
console.log("卡号-校验通过")
|
||||||
loading.value = true
|
loading.value = true
|
||||||
// 调用后端接口校验卡号
|
// 调用后端接口校验卡号
|
||||||
await userStore.cardLogin(form)
|
await userStore.cardLogin(form)
|
||||||
|
|
||||||
|
// 登录成功后获取系统配置(根据登录账号显示不同的系统名称)
|
||||||
|
await appStore.initSiteConfig()
|
||||||
|
|
||||||
// 登录成功后移除事件监听器
|
// 登录成功后移除事件监听器
|
||||||
if (keyDownListener) {
|
if (keyDownListener) {
|
||||||
document.removeEventListener('keydown', keyDownListener)
|
document.removeEventListener('keydown', keyDownListener)
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
<a-tab-pane key="1" title="账号登录">
|
<a-tab-pane key="1" title="账号登录">
|
||||||
<component :is="AccountLogin" v-if="activeTab === '1'" />
|
<component :is="AccountLogin" v-if="activeTab === '1'" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="2" title="刷卡">
|
<!-- <a-tab-pane key="2" title="刷卡">-->
|
||||||
<component :is="CardLogin" v-if="activeTab === '2'" />
|
<!-- <component :is="CardLogin" v-if="activeTab === '2'" />-->
|
||||||
</a-tab-pane>
|
<!-- </a-tab-pane>-->
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -51,7 +51,8 @@ defineOptions({ name: 'Login' })
|
|||||||
|
|
||||||
const { isDesktop } = useDevice()
|
const { isDesktop } = useDevice()
|
||||||
const appStore = useAppStore()
|
const appStore = useAppStore()
|
||||||
const title = computed(() => appStore.getTitle())
|
// 登录页面显示固定的"称重系统"
|
||||||
|
const title = '称重系统'
|
||||||
const logo = computed(() => appStore.getLogo())
|
const logo = computed(() => appStore.getLogo())
|
||||||
|
|
||||||
const isEmailLogin = ref(false)
|
const isEmailLogin = ref(false)
|
||||||
|
|||||||
163
src/views/material/BatchImport.vue
Normal file
163
src/views/material/BatchImport.vue
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="批次导入"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 600 ? 600 : '100%'"
|
||||||
|
ok-text="确认导入"
|
||||||
|
cancel-text="取消导入"
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" :model="form" size="large" auto-label-width>
|
||||||
|
<a-alert v-if="!form.disabled" style="margin-bottom: 15px">
|
||||||
|
请按照模板要求填写数据,填写完毕后,请先上传并进行解析。
|
||||||
|
<template #action>
|
||||||
|
<a-link @click="downloadTemplate">
|
||||||
|
<template #icon><GiSvgIcon name="file-excel" :size="16" /></template>
|
||||||
|
<template #default>下载模板</template>
|
||||||
|
</a-link>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
<fieldset>
|
||||||
|
<legend>1.解析数据</legend>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-upload
|
||||||
|
draggable
|
||||||
|
:custom-request="handleUpload"
|
||||||
|
:limit="1"
|
||||||
|
:show-retry-butto="false"
|
||||||
|
:show-cancel-button="false" tip="仅支持xls、xlsx格式"
|
||||||
|
:file-list="uploadFile"
|
||||||
|
accept=".xls, .xlsx, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="dataResult.importKey">
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="总计行数" :value="dataResult.totalRows" />
|
||||||
|
<a-statistic title="正常行数" :value="dataResult.validRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="已存在批次" :value="dataResult.duplicateRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</a-form>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type FormInstance, Message, type RequestOption } from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import {
|
||||||
|
type BatchImportResp,
|
||||||
|
downloadBatchImportTemplate,
|
||||||
|
batchImport,
|
||||||
|
parseBatchImport,
|
||||||
|
} from '@/apis/material/materialInfo'
|
||||||
|
import { useDownload, useResetReactive } from '@/hooks'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const uploadFile = ref([])
|
||||||
|
|
||||||
|
const [form, resetForm] = useResetReactive({
|
||||||
|
importKey: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataResult = ref<BatchImportResp>({
|
||||||
|
importKey: '',
|
||||||
|
totalRows: 0,
|
||||||
|
validRows: 0,
|
||||||
|
duplicateRows: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
dataResult.value.importKey = ''
|
||||||
|
uploadFile.value = []
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadTemplate = () => {
|
||||||
|
useDownload(() => downloadBatchImportTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUpload = (options: RequestOption) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
(async function requestWrap() {
|
||||||
|
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options
|
||||||
|
onProgress(20)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(name as string, fileItem.file as Blob)
|
||||||
|
try {
|
||||||
|
const res = await parseBatchImport(formData)
|
||||||
|
dataResult.value = res.data
|
||||||
|
Message.success('上传解析成功')
|
||||||
|
onSuccess(res)
|
||||||
|
} catch (error) {
|
||||||
|
onError(error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
return {
|
||||||
|
abort() {
|
||||||
|
controller.abort()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
if (!dataResult.value.importKey) {
|
||||||
|
Message.warning('请先上传文件,解析导入数据')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
form.importKey = dataResult.value.importKey
|
||||||
|
const res = await batchImport(form)
|
||||||
|
Message.success(`导入成功! 表格总数${res.data.totalRows}, 修改${res.data.updateRows}`)
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onOpen = () => {
|
||||||
|
reset()
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onOpen })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
fieldset {
|
||||||
|
padding: 15px 15px 0 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
color: rgb(var(--gray-10));
|
||||||
|
padding: 2px 5px 2px 5px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-box {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,6 +19,9 @@ import { useWindowSize } from '@vueuse/core'
|
|||||||
import { addMaterialInfo, getMaterialInfo, updateMaterialInfo } from '@/apis/material/materialInfo'
|
import { addMaterialInfo, getMaterialInfo, updateMaterialInfo } from '@/apis/material/materialInfo'
|
||||||
import { type ColumnItem, GiForm } from '@/components/GiForm'
|
import { type ColumnItem, GiForm } from '@/components/GiForm'
|
||||||
import { useResetReactive } from '@/hooks'
|
import { useResetReactive } from '@/hooks'
|
||||||
|
import { materialType } from "@/hooks/app/materialType";
|
||||||
|
import {materialProcess} from "@/hooks/app/materialProcess";
|
||||||
|
import {useDict} from "@/hooks/app";
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'save-success'): void
|
(e: 'save-success'): void
|
||||||
@@ -32,6 +35,11 @@ const isUpdate = computed(() => !!dataId.value)
|
|||||||
const title = computed(() => (isUpdate.value ? '修改物料管理' : '新增物料管理'))
|
const title = computed(() => (isUpdate.value ? '修改物料管理' : '新增物料管理'))
|
||||||
const formRef = ref<InstanceType<typeof GiForm>>()
|
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||||
|
|
||||||
|
const { materialTypeList, getMaterialTypeSelect } = materialType()
|
||||||
|
const { light_level } = useDict('light_level')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const [form, resetForm] = useResetReactive({
|
const [form, resetForm] = useResetReactive({
|
||||||
// todo 待补充
|
// todo 待补充
|
||||||
})
|
})
|
||||||
@@ -52,14 +60,63 @@ const columns: ColumnItem[] = reactive([
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '物料单位重量,单位(g)',
|
label: '物料单位重量(g)',
|
||||||
field: 'unitWeight',
|
field: 'unitWeight',
|
||||||
type: 'input-number',
|
type: 'input-number',
|
||||||
span: 24,
|
span: 24,
|
||||||
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '物料规格',
|
label: '物料品类',
|
||||||
|
field: 'materialTypeId',
|
||||||
|
type: 'select',
|
||||||
|
span: 24,
|
||||||
|
required: true,
|
||||||
|
props: {
|
||||||
|
options: materialTypeList,
|
||||||
|
allowClear: true,
|
||||||
|
allowSearch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '物料流程编码',
|
||||||
|
field: 'materialProcess',
|
||||||
|
type: 'input',
|
||||||
|
span: 24,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '批次',
|
||||||
|
field: 'batch',
|
||||||
|
type: 'input',
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '灯光等级',
|
||||||
|
field: 'lightLevel',
|
||||||
|
type: 'input-number',
|
||||||
|
props: {
|
||||||
|
mode: 'button',
|
||||||
|
min: 1,
|
||||||
|
max: 300,
|
||||||
|
step: 1,
|
||||||
|
},
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '物料直径',
|
||||||
field: 'materialSpec',
|
field: 'materialSpec',
|
||||||
|
type: 'input-number',
|
||||||
|
props: {
|
||||||
|
min: 0,
|
||||||
|
mode: 'button',
|
||||||
|
step: 0.001,
|
||||||
|
},
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '物料颜色',
|
||||||
|
field: 'color',
|
||||||
type: 'input',
|
type: 'input',
|
||||||
span: 24,
|
span: 24,
|
||||||
},
|
},
|
||||||
@@ -101,6 +158,9 @@ const save = async () => {
|
|||||||
const onAdd = async () => {
|
const onAdd = async () => {
|
||||||
reset()
|
reset()
|
||||||
dataId.value = ''
|
dataId.value = ''
|
||||||
|
if (!materialTypeList.value.length) {
|
||||||
|
await getMaterialTypeSelect()
|
||||||
|
}
|
||||||
visible.value = true
|
visible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +169,9 @@ const onUpdate = async (id: string) => {
|
|||||||
reset()
|
reset()
|
||||||
dataId.value = id
|
dataId.value = id
|
||||||
const { data } = await getMaterialInfo(id)
|
const { data } = await getMaterialInfo(id)
|
||||||
|
if (!materialTypeList.value.length) {
|
||||||
|
await getMaterialTypeSelect()
|
||||||
|
}
|
||||||
Object.assign(form, data)
|
Object.assign(form, data)
|
||||||
visible.value = true
|
visible.value = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
:limit="1"
|
:limit="1"
|
||||||
:show-retry-button="false"
|
:show-retry-button="false"
|
||||||
:show-cancel-button="false"
|
:show-cancel-button="false"
|
||||||
tip="仅支持zip格式"
|
tip="仅支持zip格式,请确保图片名称和物料编码一致"
|
||||||
:file-list="uploadFile"
|
:file-list="uploadFile"
|
||||||
accept=".zip"
|
accept=".zip"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,18 +10,27 @@
|
|||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:disabled-tools="['size']"
|
:disabled-tools="['size']"
|
||||||
:disabled-column-keys="['name']"
|
:disabled-column-keys="['name']"
|
||||||
|
:selected-keys="selectedKeys"
|
||||||
|
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||||
|
@select-all="selectAll"
|
||||||
|
@select="select"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
<!-- toolbar 部分保持不变 -->
|
<!-- toolbar 部分保持不变 -->
|
||||||
<template #toolbar-left>
|
<template #toolbar-left>
|
||||||
<a-input-search v-model="queryForm.materialName" placeholder="请输入物料名称" allow-clear @search="search" />
|
<a-input-search v-model="queryForm.materialName" placeholder="请输入物料名称" allow-clear @search="search" />
|
||||||
<a-input-search v-model="queryForm.encoding" placeholder="请输入物料编码" allow-clear @search="search" />
|
<a-input-search v-model="queryForm.encoding" placeholder="请输入物料编码" allow-clear @search="search" />
|
||||||
|
<a-input-search v-model="queryForm.batch" placeholder="请输入批次" allow-clear @search="search" />
|
||||||
<a-button @click="reset">
|
<a-button @click="reset">
|
||||||
<template #icon><icon-refresh /></template>
|
<template #icon><icon-refresh /></template>
|
||||||
<template #default>重置</template>
|
<template #default>重置</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #toolbar-right>
|
<template #toolbar-right>
|
||||||
|
<a-button v-permission="['admin:materialInfo:delete']" type="outline" status="danger" @click="onDelete">
|
||||||
|
<template #icon><icon-delete /></template>
|
||||||
|
<template #default>删除</template>
|
||||||
|
</a-button>
|
||||||
<a-button v-permission="['admin:materialInfo:add']" type="primary" @click="onAdd">
|
<a-button v-permission="['admin:materialInfo:add']" type="primary" @click="onAdd">
|
||||||
<template #icon><icon-plus /></template>
|
<template #icon><icon-plus /></template>
|
||||||
<template #default>新增</template>
|
<template #default>新增</template>
|
||||||
@@ -36,33 +45,23 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-permission="['admin:materialInfo:import']" @click="onPhotosImport">
|
<a-button v-permission="['admin:materialInfo:import']" @click="onPhotosImport">
|
||||||
<template #icon><icon-upload /></template>
|
<template #icon><icon-upload /></template>
|
||||||
<template #default>照片批量导入</template>
|
<template #default>照片导入</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-permission="['admin:materialInfo:import']" @click="onBatchImport">
|
||||||
|
<template #icon><icon-upload /></template>
|
||||||
|
<template #default>批次导入</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 【修改点】照片列插槽 -->
|
|
||||||
<template #photoUrl="{ record }">
|
<template #photoUrl="{ record }">
|
||||||
<div class="photo-container">
|
<a-image
|
||||||
<!-- 1. 正常显示图片:有地址 且 未标记为错误 -->
|
width="60"
|
||||||
<img
|
|
||||||
v-if="record.photoUrl && !record.photoLoadError"
|
|
||||||
:src="record.photoUrl"
|
:src="record.photoUrl"
|
||||||
alt="物料照片"
|
|
||||||
class="material-photo"
|
|
||||||
@load="handleImgLoad(record)"
|
|
||||||
@error="handleImgError(record, $event)"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 2. 无地址 -->
|
|
||||||
<span v-else-if="!record.photoUrl" class="no-photo">暂无照片</span>
|
|
||||||
|
|
||||||
<!-- 3. 地址存在但加载失败 -->
|
|
||||||
<span v-else class="photo-error">
|
|
||||||
<icon-exclamation-circle-fill class="error-icon" />
|
|
||||||
照片异常
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
<!-- <template #lightLevel="{ record }">-->
|
||||||
|
<!-- <GiCellTag :value="record.lightLevel" :dict="light_level" />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
@@ -72,7 +71,7 @@
|
|||||||
status="danger"
|
status="danger"
|
||||||
:disabled="record.disabled"
|
:disabled="record.disabled"
|
||||||
:title="record.disabled ? '不可删除' : '删除'"
|
:title="record.disabled ? '不可删除' : '删除'"
|
||||||
@click="onDelete(record)"
|
@click="onDeleteOne(record)"
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</a-link>
|
</a-link>
|
||||||
@@ -83,24 +82,26 @@
|
|||||||
<MaterialInfoAddModal ref="MaterialInfoAddModalRef" @save-success="search" />
|
<MaterialInfoAddModal ref="MaterialInfoAddModalRef" @save-success="search" />
|
||||||
<MaterialInfoImportDrawer ref="MaterialInfoImportDrawerRef" @save-success="search" />
|
<MaterialInfoImportDrawer ref="MaterialInfoImportDrawerRef" @save-success="search" />
|
||||||
<PhotosImport ref="PhotosImportRef" @save-success="search"/>
|
<PhotosImport ref="PhotosImportRef" @save-success="search"/>
|
||||||
|
<BatchImport ref="BatchImportRef" @save-success="search"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// ... 保持原有的 import 不变
|
|
||||||
import MaterialInfoAddModal from './MaterialInfoAddModal.vue'
|
import MaterialInfoAddModal from './MaterialInfoAddModal.vue'
|
||||||
import MaterialInfoImportDrawer from './MaterialInfoImportDrawer.vue'
|
import MaterialInfoImportDrawer from './MaterialInfoImportDrawer.vue'
|
||||||
import PhotosImport from '@/views/material/PhotosImport.vue';
|
import PhotosImport from '@/views/material/PhotosImport.vue';
|
||||||
|
import BatchImport from '@/views/material/BatchImport.vue';
|
||||||
import { type MaterialInfoQuery, type MaterialInfoResp, deleteMaterialInfo, exportMaterialInfo, listMaterialInfo } from '@/apis/material/materialInfo'
|
import { type MaterialInfoQuery, type MaterialInfoResp, deleteMaterialInfo, exportMaterialInfo, listMaterialInfo } from '@/apis/material/materialInfo'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import { useDownload, useTable } from '@/hooks'
|
import { useDownload, useTable } from '@/hooks'
|
||||||
import { isMobile } from '@/utils'
|
import { isMobile } from '@/utils'
|
||||||
import has from '@/utils/has'
|
import has from '@/utils/has'
|
||||||
|
import {Message} from "@arco-design/web-vue";
|
||||||
|
|
||||||
defineOptions({ name: 'MaterialInfo' })
|
defineOptions({ name: 'MaterialInfo' })
|
||||||
|
|
||||||
// 扩展类型定义,允许动态添加 photoLoadError 属性
|
// const { light_level } = useDict('light_level')
|
||||||
// 如果 TS 报错,可以在 MaterialInfoResp 接口定义中添加 photoLoadError?: boolean;
|
|
||||||
interface MaterialInfoRespWithStatus extends MaterialInfoResp {
|
interface MaterialInfoRespWithStatus extends MaterialInfoResp {
|
||||||
photoLoadError?: boolean;
|
photoLoadError?: boolean;
|
||||||
}
|
}
|
||||||
@@ -108,13 +109,18 @@ interface MaterialInfoRespWithStatus extends MaterialInfoResp {
|
|||||||
const queryForm = reactive<MaterialInfoQuery>({
|
const queryForm = reactive<MaterialInfoQuery>({
|
||||||
materialName: undefined,
|
materialName: undefined,
|
||||||
encoding: undefined,
|
encoding: undefined,
|
||||||
sort: ['id,desc'],
|
batch: undefined,
|
||||||
|
mark: undefined,
|
||||||
|
sort: ['mi.id,desc'],
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tableData: dataList,
|
tableData: dataList,
|
||||||
loading,
|
loading,
|
||||||
pagination,
|
pagination,
|
||||||
|
selectedKeys,
|
||||||
|
select,
|
||||||
|
selectAll,
|
||||||
search,
|
search,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
} = useTable((page) => listMaterialInfo({ ...queryForm, ...page }), { immediate: true })
|
} = useTable((page) => listMaterialInfo({ ...queryForm, ...page }), { immediate: true })
|
||||||
@@ -123,7 +129,12 @@ const columns = ref<TableInstanceColumns[]>([
|
|||||||
{ title: '物料名称', dataIndex: 'materialName', slotName: 'materialName' },
|
{ title: '物料名称', dataIndex: 'materialName', slotName: 'materialName' },
|
||||||
{ title: '物料编码', dataIndex: 'encoding', slotName: 'encoding' },
|
{ title: '物料编码', dataIndex: 'encoding', slotName: 'encoding' },
|
||||||
{ title: '物料单位重量(g)', dataIndex: 'unitWeight', slotName: 'unitWeight' },
|
{ title: '物料单位重量(g)', dataIndex: 'unitWeight', slotName: 'unitWeight' },
|
||||||
{ title: '物料规格', dataIndex: 'materialSpec', slotName: 'materialSpec' },
|
{ title: '物料品类', dataIndex: 'typeName' },
|
||||||
|
{ title: '批次', dataIndex: 'batch' },
|
||||||
|
{ title: '物料直径', dataIndex: 'materialSpec', slotName: 'materialSpec', show: false },
|
||||||
|
{ title: '物料颜色', dataIndex: 'color', slotName: 'color', show: false },
|
||||||
|
{ title: '物料流程', dataIndex: 'materialProcess', slotName: 'materialProcess' },
|
||||||
|
{ title: '灯光等级', dataIndex: 'lightLevel', slotName: 'lightLevel' },
|
||||||
{ title: '物料照片', dataIndex: 'photoUrl', slotName: 'photoUrl', width: 120, align: 'center' },
|
{ title: '物料照片', dataIndex: 'photoUrl', slotName: 'photoUrl', width: 120, align: 'center' },
|
||||||
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' },
|
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' },
|
||||||
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime' },
|
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime' },
|
||||||
@@ -144,13 +155,24 @@ const reset = () => {
|
|||||||
search()
|
search()
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDelete = (record: MaterialInfoResp) => {
|
const onDeleteOne = (record: MaterialInfoResp) => {
|
||||||
return handleDelete(() => deleteMaterialInfo(record.id), {
|
return handleDelete(() => deleteMaterialInfo(record.id), {
|
||||||
content: `是否确定删除该条数据?`,
|
content: `是否确定删除该条数据?`,
|
||||||
showModal: true,
|
showModal: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const onDelete = () => {
|
||||||
|
if (!selectedKeys.value.length) {
|
||||||
|
return Message.warning('请选择数据')
|
||||||
|
}
|
||||||
|
return handleDelete(() => deleteMaterialInfo(selectedKeys.value), {
|
||||||
|
content: `是否确定删除选中的 ${selectedKeys.value.length} 条数据?`,
|
||||||
|
showModal: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const onExport = () => {
|
const onExport = () => {
|
||||||
useDownload(() => exportMaterialInfo(queryForm))
|
useDownload(() => exportMaterialInfo(queryForm))
|
||||||
}
|
}
|
||||||
@@ -184,6 +206,12 @@ const PhotosImportRef = ref<InstanceType<typeof PhotosImport>>()
|
|||||||
const onPhotosImport = () => {
|
const onPhotosImport = () => {
|
||||||
PhotosImportRef.value?.onOpen()
|
PhotosImportRef.value?.onOpen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BatchImportRef = ref<InstanceType<typeof BatchImport>>()
|
||||||
|
const onBatchImport = () => {
|
||||||
|
BatchImportRef.value?.onOpen()
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
103
src/views/materialProcess/MaterialProcessAddModal.vue
Normal file
103
src/views/materialProcess/MaterialProcessAddModal.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:title="title"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 600 ? 600 : '100%'"
|
||||||
|
draggable
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<GiForm ref="formRef" v-model="form" :columns="columns" />
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import {
|
||||||
|
addMaterialProcess,
|
||||||
|
updateMaterialProcess,
|
||||||
|
type MaterialProcessResp
|
||||||
|
} from '@/apis/materialProcess/materialProcess'
|
||||||
|
import { type ColumnItem, GiForm } from '@/components/GiForm'
|
||||||
|
import { useResetReactive } from '@/hooks'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const dataId = ref('')
|
||||||
|
const visible = ref(false)
|
||||||
|
const isUpdate = computed(() => !!dataId.value)
|
||||||
|
const title = computed(() => (isUpdate.value ? '修改海康物料流程' : '新增海康物料流程'))
|
||||||
|
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||||
|
|
||||||
|
const [form, resetForm] = useResetReactive({
|
||||||
|
// todo 待补充
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns: ColumnItem[] = reactive([
|
||||||
|
{
|
||||||
|
label: '流程名称',
|
||||||
|
field: 'processName',
|
||||||
|
type: 'input',
|
||||||
|
span: 24,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '流程编码',
|
||||||
|
field: 'processCode',
|
||||||
|
type: 'input',
|
||||||
|
span: 24,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
formRef.value?.formRef?.resetFields()
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
const isInvalid = await formRef.value?.formRef?.validate()
|
||||||
|
if (isInvalid) return false
|
||||||
|
if (isUpdate.value) {
|
||||||
|
await updateMaterialProcess(form, dataId.value)
|
||||||
|
Message.success('修改成功')
|
||||||
|
} else {
|
||||||
|
await addMaterialProcess(form)
|
||||||
|
Message.success('新增成功')
|
||||||
|
}
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const onAdd = async () => {
|
||||||
|
reset()
|
||||||
|
dataId.value = ''
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
const onUpdate = async (record: MaterialProcessResp) => {
|
||||||
|
reset()
|
||||||
|
dataId.value = record.id
|
||||||
|
Object.assign(form, record)
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onAdd, onUpdate })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
185
src/views/materialProcess/MaterialProcessImportDrawer.vue
Normal file
185
src/views/materialProcess/MaterialProcessImportDrawer.vue
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="导入物料流程"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 600 ? 600 : '100%'"
|
||||||
|
ok-text="确认导入"
|
||||||
|
cancel-text="取消导入"
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" :model="form" size="large" auto-label-width>
|
||||||
|
<a-alert v-if="!form.disabled" style="margin-bottom: 15px">
|
||||||
|
请按照模板要求填写数据,填写完毕后,请先上传并进行解析。
|
||||||
|
<template #action>
|
||||||
|
<a-link @click="downloadTemplate">
|
||||||
|
<template #icon><icon-download /></template>
|
||||||
|
<template #default>下载模板</template>
|
||||||
|
</a-link>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
<fieldset>
|
||||||
|
<legend>1.解析数据</legend>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-upload
|
||||||
|
draggable
|
||||||
|
:custom-request="handleUpload"
|
||||||
|
:limit="1"
|
||||||
|
:show-retry-butto="false"
|
||||||
|
:show-cancel-button="false" tip="仅支持xls、xlsx格式"
|
||||||
|
:file-list="uploadFile"
|
||||||
|
accept=".xls, .xlsx, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="dataResult.importKey">
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="总计行数" :value="dataResult.totalRows" />
|
||||||
|
<a-statistic title="正常行数" :value="dataResult.validRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="已存在流程名称" :value="dataResult.duplicateNameRows" />
|
||||||
|
<a-statistic title="已存在流程编码" :value="dataResult.duplicateCodeRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>2.导入策略</legend>
|
||||||
|
<a-form-item label="流程名称已存在" field="duplicateName">
|
||||||
|
<a-radio-group v-model="form.duplicateName" type="button">
|
||||||
|
<a-radio :value="1">跳过该行</a-radio>
|
||||||
|
<a-radio :value="2">修改数据</a-radio>
|
||||||
|
<a-radio :value="3">停止导入</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="流程编码已存在" field="duplicateCode">
|
||||||
|
<a-radio-group v-model="form.duplicateCode" type="button">
|
||||||
|
<a-radio :value="1">跳过该行</a-radio>
|
||||||
|
<a-radio :value="2">修改数据</a-radio>
|
||||||
|
<a-radio :value="3">停止导入</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
</fieldset>
|
||||||
|
</a-form>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type FormInstance, Message, type RequestOption } from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import { downloadMaterialProcessImportTemplate, importMaterialProcess, parseImportMaterialProcess } from '@/apis/materialProcess/materialProcess'
|
||||||
|
import { useDownload, useResetReactive } from '@/hooks'
|
||||||
|
import { IconDownload as iconDownload } from '@arco-design/web-vue/es/icon'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const uploadFile = ref([])
|
||||||
|
|
||||||
|
const [form, resetForm] = useResetReactive({
|
||||||
|
importKey: '',
|
||||||
|
duplicateName: 1,
|
||||||
|
duplicateCode: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataResult = ref({
|
||||||
|
importKey: '',
|
||||||
|
totalRows: 0,
|
||||||
|
validRows: 0,
|
||||||
|
duplicateNameRows: 0,
|
||||||
|
duplicateCodeRows: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
dataResult.value.importKey = ''
|
||||||
|
uploadFile.value = []
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载模板
|
||||||
|
const downloadTemplate = () => {
|
||||||
|
useDownload(() => downloadMaterialProcessImportTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传解析导入数据
|
||||||
|
const handleUpload = (options: RequestOption) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
(async function requestWrap() {
|
||||||
|
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options
|
||||||
|
onProgress(20)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(name as string, fileItem.file as Blob)
|
||||||
|
try {
|
||||||
|
const res = await parseImportMaterialProcess(formData)
|
||||||
|
dataResult.value = res.data
|
||||||
|
Message.success('上传解析成功')
|
||||||
|
onSuccess(res)
|
||||||
|
} catch (error) {
|
||||||
|
onError(error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
return {
|
||||||
|
abort() {
|
||||||
|
controller.abort()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行导入
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
if (!dataResult.value.importKey) {
|
||||||
|
Message.warning('请先上传文件,解析导入数据')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
form.importKey = dataResult.value.importKey
|
||||||
|
const res = await importMaterialProcess(form)
|
||||||
|
Message.success(`导入成功! 新增${res.data.insertCount}, 修改${res.data.updateCount},总计处理${res.data.totalHandleCount}`)
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开
|
||||||
|
const onOpen = () => {
|
||||||
|
reset()
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onOpen })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
fieldset {
|
||||||
|
padding: 15px 15px 0 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
color: rgb(var(--gray-10));
|
||||||
|
padding: 2px 5px 2px 5px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-box {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
166
src/views/materialProcess/index.vue
Normal file
166
src/views/materialProcess/index.vue
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gi_table_page">
|
||||||
|
<GiTable
|
||||||
|
title="物料流程管理"
|
||||||
|
row-key="id"
|
||||||
|
:data="dataList"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
|
:pagination="pagination"
|
||||||
|
:disabled-tools="['size']"
|
||||||
|
:disabled-column-keys="['name']"
|
||||||
|
:selected-keys="selectedKeys"
|
||||||
|
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||||
|
@select-all="selectAll"
|
||||||
|
@select="select"
|
||||||
|
@refresh="search"
|
||||||
|
>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<a-input-search v-model="queryForm.processName" placeholder="请输入流程名称" allow-clear @search="search" />
|
||||||
|
<a-input-search v-model="queryForm.processCode" placeholder="请输入流程编码" allow-clear @search="search" />
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
<template #default>重置</template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #toolbar-right>
|
||||||
|
<a-button v-permission="['materialProcess:materialProcess:delete']" type="outline" status="danger" @click="onDelete">
|
||||||
|
<template #icon><icon-delete /></template>
|
||||||
|
<template #default>删除</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-permission="['materialProcess:materialProcess:add']" type="primary" @click="onAdd">
|
||||||
|
<template #icon><icon-plus /></template>
|
||||||
|
<template #default>新增</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-permission="['materialProcess:materialProcess:import']" @click="onImport">
|
||||||
|
<template #icon><icon-upload /></template>
|
||||||
|
<template #default>导入</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-permission="['materialProcess:materialProcess:export']" @click="onExport">
|
||||||
|
<template #icon><icon-download /></template>
|
||||||
|
<template #default>导出</template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #action="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<a-link v-permission="['materialProcess:materialProcess:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
||||||
|
<a-link
|
||||||
|
v-permission="['materialProcess:materialProcess:delete']"
|
||||||
|
status="danger"
|
||||||
|
:disabled="record.disabled"
|
||||||
|
:title="record.disabled ? '不可删除' : '删除'"
|
||||||
|
@click="onDeleteOne(record)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</a-link>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</GiTable>
|
||||||
|
|
||||||
|
<MaterialProcessAddModal ref="MaterialProcessAddModalRef" @save-success="search" />
|
||||||
|
<MaterialProcessImportDrawer ref="MaterialProcessImportDrawerRef" @save-success="search" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import MaterialProcessAddModal from './MaterialProcessAddModal.vue'
|
||||||
|
import MaterialProcessImportDrawer from './MaterialProcessImportDrawer.vue'
|
||||||
|
import { type MaterialProcessResp, type MaterialProcessQuery, deleteMaterialProcess, exportMaterialProcess, listMaterialProcess } from '@/apis/materialProcess/materialProcess'
|
||||||
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
|
import { useDownload, useTable } from '@/hooks'
|
||||||
|
import { isMobile } from '@/utils'
|
||||||
|
import has from '@/utils/has'
|
||||||
|
import { IconUpload as iconUpload, IconDownload as iconDownload } from '@arco-design/web-vue/es/icon'
|
||||||
|
import {Message} from "@arco-design/web-vue";
|
||||||
|
|
||||||
|
defineOptions({ name: 'MaterialProcess' })
|
||||||
|
|
||||||
|
|
||||||
|
const queryForm = reactive<MaterialProcessQuery>({
|
||||||
|
processName: undefined,
|
||||||
|
processCode: undefined,
|
||||||
|
sort: ['id,desc']
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
tableData: dataList,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
selectedKeys,
|
||||||
|
selectAll,
|
||||||
|
select,
|
||||||
|
search,
|
||||||
|
handleDelete
|
||||||
|
} = useTable((page) => listMaterialProcess({ ...queryForm, ...page }), { immediate: true })
|
||||||
|
const columns = ref<TableInstanceColumns[]>([
|
||||||
|
{ title: '流程名称', dataIndex: 'processName', slotName: 'processName' },
|
||||||
|
{ title: '流程编码', dataIndex: 'processCode', slotName: 'processCode' },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime' },
|
||||||
|
{ title: '更新时间', dataIndex: 'updateTime', slotName: 'updateTime', show: false },
|
||||||
|
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' },
|
||||||
|
{ title: '更新人', dataIndex: 'updateUserString', slotName: 'updateUser', show: false },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
slotName: 'action',
|
||||||
|
width: 160,
|
||||||
|
align: 'center',
|
||||||
|
fixed: !isMobile() ? 'right' : undefined,
|
||||||
|
show: has.hasPermOr(['materialProcess:materialProcess:detail', 'materialProcess:materialProcess:update', 'materialProcess:materialProcess:delete'])
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
queryForm.processName = undefined
|
||||||
|
queryForm.processCode = undefined
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const onDeleteOne = (record: MaterialProcessResp) => {
|
||||||
|
return handleDelete(() => deleteMaterialProcess(record.id), {
|
||||||
|
content: `是否确定删除该条数据?`,
|
||||||
|
showModal: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const onDelete = () => {
|
||||||
|
if (!selectedKeys.value.length) {
|
||||||
|
return Message.warning('请选择数据')
|
||||||
|
}
|
||||||
|
return handleDelete(() => deleteMaterialProcess(selectedKeys.value), {
|
||||||
|
content: `是否确定删除选中的 ${selectedKeys.value.length} 条数据?`,
|
||||||
|
showModal: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const onExport = () => {
|
||||||
|
useDownload(() => exportMaterialProcess(queryForm))
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaterialProcessAddModalRef = ref<InstanceType<typeof MaterialProcessAddModal>>()
|
||||||
|
const MaterialProcessImportDrawerRef = ref<InstanceType<typeof MaterialProcessImportDrawer>>()
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const onAdd = () => {
|
||||||
|
MaterialProcessAddModalRef.value?.onAdd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
const onUpdate = (record: MaterialProcessResp) => {
|
||||||
|
MaterialProcessAddModalRef.value?.onUpdate(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
const onImport = () => {
|
||||||
|
MaterialProcessImportDrawerRef.value?.onOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
122
src/views/materialType/MaterialTypeAddModal.vue
Normal file
122
src/views/materialType/MaterialTypeAddModal.vue
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="visible"
|
||||||
|
:title="title"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 600 ? 600 : '100%'"
|
||||||
|
draggable
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<GiForm ref="formRef" v-model="form" :columns="columns">
|
||||||
|
<template #floatRatio>
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px;">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.downFloatRatio"
|
||||||
|
style="width: 45%"
|
||||||
|
:min="-100"
|
||||||
|
:max="100"
|
||||||
|
:mode="'button'"
|
||||||
|
placeholder="最小值"
|
||||||
|
required: true,
|
||||||
|
/>
|
||||||
|
<span style="color: #999;">至</span>
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.upFloatRatio"
|
||||||
|
style="width: 45%"
|
||||||
|
:min="0"
|
||||||
|
:max="100"
|
||||||
|
:mode="'button'"
|
||||||
|
placeholder="最大值"
|
||||||
|
required: true,
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</GiForm>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import { addMaterialType, type MaterialTypeResp, updateMaterialType} from '@/apis/materialType/materialType'
|
||||||
|
import { type ColumnItem, GiForm } from '@/components/GiForm'
|
||||||
|
import { useResetReactive } from '@/hooks'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const dataId = ref('')
|
||||||
|
const visible = ref(false)
|
||||||
|
const isUpdate = computed(() => !!dataId.value)
|
||||||
|
const title = computed(() => (isUpdate.value ? '修改物料品类' : '新增物料品类'))
|
||||||
|
const formRef = ref<InstanceType<typeof GiForm>>()
|
||||||
|
|
||||||
|
const [form, resetForm] = useResetReactive({
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns: ColumnItem[] = reactive([
|
||||||
|
{
|
||||||
|
label: '品类名称',
|
||||||
|
field: 'typeName',
|
||||||
|
type: 'input',
|
||||||
|
span: 24,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '浮动范围(%)',
|
||||||
|
field: 'floatRatio',
|
||||||
|
type: 'custom',
|
||||||
|
span: 24,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
formRef.value?.formRef?.resetFields()
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
const isInvalid = await formRef.value?.formRef?.validate()
|
||||||
|
if (isInvalid) return false
|
||||||
|
if (isUpdate.value) {
|
||||||
|
await updateMaterialType(form, dataId.value)
|
||||||
|
Message.success('修改成功')
|
||||||
|
} else {
|
||||||
|
await addMaterialType(form)
|
||||||
|
Message.success('新增成功')
|
||||||
|
}
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const onAdd = async () => {
|
||||||
|
reset()
|
||||||
|
dataId.value = ''
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
const onUpdate = async (data: MaterialTypeResp) => {
|
||||||
|
reset()
|
||||||
|
dataId.value = data.id
|
||||||
|
Object.assign(form, data)
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onAdd, onUpdate })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
179
src/views/materialType/MaterialTypeImportDrawer.vue
Normal file
179
src/views/materialType/MaterialTypeImportDrawer.vue
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<a-drawer
|
||||||
|
v-model:visible="visible"
|
||||||
|
title="导入物料品类"
|
||||||
|
:mask-closable="false"
|
||||||
|
:esc-to-close="false"
|
||||||
|
:width="width >= 600 ? 600 : '100%'"
|
||||||
|
ok-text="确认导入"
|
||||||
|
cancel-text="取消导入"
|
||||||
|
@before-ok="save"
|
||||||
|
@close="reset"
|
||||||
|
>
|
||||||
|
<a-form ref="formRef" :model="form" size="large" auto-label-width>
|
||||||
|
<a-alert v-if="!form.disabled" style="margin-bottom: 15px">
|
||||||
|
请按照模板要求填写数据,填写完毕后,请先上传并进行解析。
|
||||||
|
<template #action>
|
||||||
|
<a-link @click="downloadTemplate">
|
||||||
|
<template #icon><GiSvgIcon name="file-excel" :size="16" /></template>
|
||||||
|
<template #default>下载模板</template>
|
||||||
|
</a-link>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
<fieldset>
|
||||||
|
<legend>1.解析数据</legend>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-upload
|
||||||
|
draggable
|
||||||
|
:custom-request="handleUpload"
|
||||||
|
:limit="1"
|
||||||
|
:show-retry-butto="false"
|
||||||
|
:show-cancel-button="false" tip="仅支持xls、xlsx格式"
|
||||||
|
:file-list="uploadFile"
|
||||||
|
accept=".xls, .xlsx, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="dataResult.importKey">
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="总计行数" :value="dataResult.totalRows" />
|
||||||
|
<a-statistic title="正常行数" :value="dataResult.validRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div class="file-box">
|
||||||
|
<a-space size="large">
|
||||||
|
<a-statistic title="已存在品类名" :value="dataResult.duplicateNameRows" />
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>2.导入策略</legend>
|
||||||
|
<a-form-item label="品类名已存在" field="duplicateTypeName">
|
||||||
|
<a-radio-group v-model="form.duplicateTypeName" type="button">
|
||||||
|
<a-radio :value="1">跳过该行</a-radio>
|
||||||
|
<a-radio :value="2">修改数据</a-radio>
|
||||||
|
<a-radio :value="3">停止导入</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
</fieldset>
|
||||||
|
</a-form>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { type FormInstance, Message, type RequestOption } from '@arco-design/web-vue'
|
||||||
|
import { useWindowSize } from '@vueuse/core'
|
||||||
|
import {
|
||||||
|
type MaterialTypeImportResp,
|
||||||
|
downloadMaterialTypeImportTemplate,
|
||||||
|
importMaterialType,
|
||||||
|
parseImportMaterialType,
|
||||||
|
} from '@/apis/materialType/materialType'
|
||||||
|
import { useDownload, useResetReactive } from '@/hooks'
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'save-success'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { width } = useWindowSize()
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
const uploadFile = ref([])
|
||||||
|
|
||||||
|
const [form, resetForm] = useResetReactive({
|
||||||
|
importKey: '',
|
||||||
|
duplicateTypeName: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const dataResult = ref<MaterialTypeImportResp>({
|
||||||
|
importKey: '',
|
||||||
|
totalRows: 0,
|
||||||
|
validRows: 0,
|
||||||
|
duplicateNameRows: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
dataResult.value.importKey = ''
|
||||||
|
uploadFile.value = []
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载模板
|
||||||
|
const downloadTemplate = () => {
|
||||||
|
useDownload(() => downloadMaterialTypeImportTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传解析导入数据
|
||||||
|
const handleUpload = (options: RequestOption) => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
(async function requestWrap() {
|
||||||
|
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options
|
||||||
|
onProgress(20)
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append(name as string, fileItem.file as Blob)
|
||||||
|
try {
|
||||||
|
const res = await parseImportMaterialType(formData)
|
||||||
|
dataResult.value = res.data
|
||||||
|
Message.success('上传解析成功')
|
||||||
|
onSuccess(res)
|
||||||
|
} catch (error) {
|
||||||
|
onError(error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
return {
|
||||||
|
abort() {
|
||||||
|
controller.abort()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行导入
|
||||||
|
const save = async () => {
|
||||||
|
try {
|
||||||
|
if (!dataResult.value.importKey) {
|
||||||
|
Message.warning('请先上传文件,解析导入数据')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
form.importKey = dataResult.value.importKey
|
||||||
|
const res = await importMaterialType(form)
|
||||||
|
Message.success(`导入成功! 新增${res.data.insertRows}, 修改${res.data.updateRows},总计处理${res.data.totalRows}`)
|
||||||
|
emit('save-success')
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开
|
||||||
|
const onOpen = () => {
|
||||||
|
reset()
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ onOpen })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
fieldset {
|
||||||
|
padding: 15px 15px 0 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset legend {
|
||||||
|
color: rgb(var(--gray-10));
|
||||||
|
padding: 2px 5px 2px 5px;
|
||||||
|
border: 1px solid var(--color-neutral-3);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-box {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
149
src/views/materialType/index.vue
Normal file
149
src/views/materialType/index.vue
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gi_table_page">
|
||||||
|
<GiTable
|
||||||
|
title="物料品类管理"
|
||||||
|
row-key="id"
|
||||||
|
:data="dataList"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
|
:pagination="pagination"
|
||||||
|
:disabled-tools="['size']"
|
||||||
|
:disabled-column-keys="['name']"
|
||||||
|
:selected-keys="selectedKeys"
|
||||||
|
:row-selection="{ type: 'checkbox', showCheckedAll: true }"
|
||||||
|
@select-all="selectAll"
|
||||||
|
@select="select"
|
||||||
|
@refresh="search"
|
||||||
|
>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<a-input-search v-model="queryForm.typeName" placeholder="请输入品类名称" allow-clear @search="search" />
|
||||||
|
<a-button @click="reset">
|
||||||
|
<template #icon><icon-refresh /></template>
|
||||||
|
<template #default>重置</template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
<template #toolbar-right>
|
||||||
|
<a-button v-permission="['materialType:materialType:add']" type="primary" @click="onAdd">
|
||||||
|
<template #icon><icon-plus /></template>
|
||||||
|
<template #default>新增</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-permission="['materialType:materialType:import']" @click="onImport">
|
||||||
|
<template #icon><icon-upload /></template>
|
||||||
|
<template #default>导入</template>
|
||||||
|
</a-button>
|
||||||
|
<a-button v-permission="['materialType:materialType:export']" @click="onExport">
|
||||||
|
<template #icon><icon-download /></template>
|
||||||
|
<template #default>导出</template>
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #floatRatio="{ record }">
|
||||||
|
{{ record.downFloatRatio + '% ~ ' + record.upFloatRatio + '%' }}
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #action="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<a-link v-permission="['materialType:materialType:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
||||||
|
<a-link
|
||||||
|
v-permission="['materialType:materialType:delete']"
|
||||||
|
status="danger"
|
||||||
|
@click="onDelete(record.id)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</a-link>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</GiTable>
|
||||||
|
|
||||||
|
<MaterialTypeAddModal ref="MaterialTypeAddModalRef" @save-success="search" />
|
||||||
|
<MaterialTypeImportDrawer ref="MaterialTypeImportDrawerRef" @save-success="search" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { h } from 'vue'
|
||||||
|
import MaterialTypeAddModal from './MaterialTypeAddModal.vue'
|
||||||
|
import MaterialTypeImportDrawer from './MaterialTypeImportDrawer.vue'
|
||||||
|
import { type MaterialTypeResp, type MaterialTypeQuery, deleteMaterialType, exportMaterialType, listMaterialType } from '@/apis/materialType/materialType'
|
||||||
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
|
import { useDownload, useTable } from '@/hooks'
|
||||||
|
import { isMobile } from '@/utils'
|
||||||
|
import has from '@/utils/has'
|
||||||
|
|
||||||
|
defineOptions({ name: 'MaterialType' })
|
||||||
|
|
||||||
|
|
||||||
|
const queryForm = reactive<MaterialTypeQuery>({
|
||||||
|
typeName: undefined,
|
||||||
|
sort: ['id,desc']
|
||||||
|
})
|
||||||
|
|
||||||
|
const {
|
||||||
|
tableData: dataList,
|
||||||
|
loading,
|
||||||
|
pagination,
|
||||||
|
selectedKeys,
|
||||||
|
select,
|
||||||
|
selectAll,
|
||||||
|
search,
|
||||||
|
handleDelete
|
||||||
|
} = useTable((page) => listMaterialType({ ...queryForm, ...page }), { immediate: true })
|
||||||
|
const columns = ref<TableInstanceColumns[]>([
|
||||||
|
{ title: '品类名称', dataIndex: 'typeName', slotName: 'typeName' },
|
||||||
|
{ title: '品类上下浮动范围(%)', dataIndex: 'upFloatRatio', slotName: 'floatRatio' },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime' },
|
||||||
|
{ title: '更新时间', dataIndex: 'updateTime', slotName: 'updateTime', show: false },
|
||||||
|
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' },
|
||||||
|
{ title: '更新人', dataIndex: 'updateUserString', slotName: 'updateUser', show: false },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
slotName: 'action',
|
||||||
|
width: 160,
|
||||||
|
align: 'center',
|
||||||
|
fixed: !isMobile() ? 'right' : undefined,
|
||||||
|
show: has.hasPermOr(['materialType:materialType:detail', 'materialType:materialType:update', 'materialType:materialType:delete'])
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 重置
|
||||||
|
const reset = () => {
|
||||||
|
queryForm.typeName = undefined
|
||||||
|
search()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const onDelete = (id) => {
|
||||||
|
return handleDelete(() => deleteMaterialType(id), {
|
||||||
|
content: `是否确定删除该条数据?`,
|
||||||
|
showModal: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const onExport = () => {
|
||||||
|
useDownload(() => exportMaterialType(queryForm))
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaterialTypeAddModalRef = ref<InstanceType<typeof MaterialTypeAddModal>>()
|
||||||
|
const MaterialTypeImportDrawerRef = ref<InstanceType<typeof MaterialTypeImportDrawer>>()
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const onAdd = () => {
|
||||||
|
MaterialTypeAddModalRef.value?.onAdd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
const onUpdate = (record: MaterialTypeResp) => {
|
||||||
|
MaterialTypeAddModalRef.value?.onUpdate(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入
|
||||||
|
const onImport = () => {
|
||||||
|
MaterialTypeImportDrawerRef.value?.onOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -20,7 +20,7 @@ import { type ColumnItem, GiForm } from '@/components/GiForm'
|
|||||||
import type { Gender, Status } from '@/types/global'
|
import type { Gender, Status } from '@/types/global'
|
||||||
import { GenderList } from '@/constant/common'
|
import { GenderList } from '@/constant/common'
|
||||||
import { useResetReactive } from '@/hooks'
|
import { useResetReactive } from '@/hooks'
|
||||||
import { useDept, useRole } from '@/hooks/app'
|
import {useDept, useDict, useRole} from '@/hooks/app'
|
||||||
import { encryptByRsa } from '@/utils/encrypt'
|
import { encryptByRsa } from '@/utils/encrypt'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -28,6 +28,7 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
|
const { data_source } = useDict('data_source')
|
||||||
|
|
||||||
const dataId = ref('')
|
const dataId = ref('')
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
@@ -102,6 +103,17 @@ const columns: ColumnItem[] = reactive([
|
|||||||
allowSearch: true,
|
allowSearch: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '系统数据源',
|
||||||
|
field: 'dataSource',
|
||||||
|
type: 'select',
|
||||||
|
span: 24,
|
||||||
|
required: true,
|
||||||
|
props: {
|
||||||
|
options: data_source,
|
||||||
|
allowClear: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '描述',
|
label: '描述',
|
||||||
field: 'description',
|
field: 'description',
|
||||||
|
|||||||
@@ -55,6 +55,9 @@
|
|||||||
<a-tag v-if="record.isSystem" color="red" size="small">是</a-tag>
|
<a-tag v-if="record.isSystem" color="red" size="small">是</a-tag>
|
||||||
<a-tag v-else color="arcoblue" size="small">否</a-tag>
|
<a-tag v-else color="arcoblue" size="small">否</a-tag>
|
||||||
</template>
|
</template>
|
||||||
|
<template #dataSource="{ record }">
|
||||||
|
<GiCellTag :value="record.dataSource" :dict="data_source" />
|
||||||
|
</template>
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-link v-permission="['system:user:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
<a-link v-permission="['system:user:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
||||||
@@ -103,9 +106,12 @@ import { useDownload, useResetReactive, useTable } from '@/hooks'
|
|||||||
import { isMobile } from '@/utils'
|
import { isMobile } from '@/utils'
|
||||||
import has from '@/utils/has'
|
import has from '@/utils/has'
|
||||||
import type {ColumnItem} from "@/components/GiForm";
|
import type {ColumnItem} from "@/components/GiForm";
|
||||||
|
import {useDict} from "@/hooks/app";
|
||||||
|
|
||||||
defineOptions({ name: 'SystemUser' })
|
defineOptions({ name: 'SystemUser' })
|
||||||
|
|
||||||
|
const { data_source } = useDict('data_source')
|
||||||
|
|
||||||
// 查询表单
|
// 查询表单
|
||||||
const [queryForm, resetForm] = useResetReactive({
|
const [queryForm, resetForm] = useResetReactive({
|
||||||
description: undefined,
|
description: undefined,
|
||||||
@@ -173,6 +179,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
{ title: '性别', dataIndex: 'gender', slotName: 'gender', align: 'center' },
|
{ title: '性别', dataIndex: 'gender', slotName: 'gender', align: 'center' },
|
||||||
{ title: '角色', dataIndex: 'roleNames', slotName: 'roleNames', minWidth: 165 },
|
{ title: '角色', dataIndex: 'roleNames', slotName: 'roleNames', minWidth: 165 },
|
||||||
{ title: '手机号', dataIndex: 'phone', minWidth: 170, ellipsis: true, tooltip: true },
|
{ title: '手机号', dataIndex: 'phone', minWidth: 170, ellipsis: true, tooltip: true },
|
||||||
|
{ title: '系统数据源', dataIndex: 'dataSource', slotName: 'dataSource',minWidth: 170, ellipsis: true, tooltip: true },
|
||||||
{ title: '系统内置', dataIndex: 'isSystem', slotName: 'isSystem', width: 100, align: 'center', show: false },
|
{ title: '系统内置', dataIndex: 'isSystem', slotName: 'isSystem', width: 100, align: 'center', show: false },
|
||||||
{ title: '描述', dataIndex: 'description', minWidth: 130, ellipsis: true, tooltip: true },
|
{ title: '描述', dataIndex: 'description', minWidth: 130, ellipsis: true, tooltip: true },
|
||||||
{ title: '创建人', dataIndex: 'createUserString', width: 140, ellipsis: true, tooltip: true, show: false },
|
{ title: '创建人', dataIndex: 'createUserString', width: 140, ellipsis: true, tooltip: true, show: false },
|
||||||
|
|||||||
666
src/views/weightManage/LabelPrint.vue
Normal file
666
src/views/weightManage/LabelPrint.vue
Normal file
@@ -0,0 +1,666 @@
|
|||||||
|
<template>
|
||||||
|
<div class="label-print-container">
|
||||||
|
|
||||||
|
<div class="form-section">
|
||||||
|
<a-form :model="formData" layout="vertical">
|
||||||
|
<div class="form-grid">
|
||||||
|
<div class="form-grid-item">
|
||||||
|
<a-form-item label="物料名称">
|
||||||
|
<a-input v-model="formData.materialName" placeholder="未获取到物料名称" :disabled="true" />
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-grid-item">
|
||||||
|
<a-form-item label="物料编码">
|
||||||
|
<a-input v-model="formData.encoding" placeholder="未获取到物料编码" :disabled="true" />
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-grid-item">
|
||||||
|
<a-form-item label="工单编号">
|
||||||
|
<a-input v-model="formData.orderNo" placeholder="未获取到工单编号" :disabled="true" />
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
<div class="form-grid-item">
|
||||||
|
<a-form-item label="生产批次">
|
||||||
|
<a-input v-model="formData.batch" placeholder="未获取到生产批次" :disabled="true" />
|
||||||
|
</a-form-item>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
<div class="form-actions">
|
||||||
|
<a-button @click="handlePrevious">继续称重</a-button>
|
||||||
|
<a-button type="primary" @click="generateDetailLabel">明细标签</a-button>
|
||||||
|
<a-button type="primary" @click="generateOverallLabel">整体标签</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="labelDataList.length > 0 || labelData.partName" class="label-preview-section">
|
||||||
|
<h3>标签预览</h3>
|
||||||
|
<div class="label-container" ref="labelContainer">
|
||||||
|
<template v-if="labelDataList.length > 0">
|
||||||
|
<div class="label" v-for="(item, index) in labelDataList" :key="index">
|
||||||
|
<table class="label-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件名称</div>
|
||||||
|
<div class="label-value">{{ item.partName }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">生产日期</div>
|
||||||
|
<div class="label-value">{{ item.productionDate }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img v-if="item.qrCodeImage" :src="item.qrCodeImage" alt="QR Code" />
|
||||||
|
<div class="mark-number">{{ item.mark || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件号</div>
|
||||||
|
<div class="label-value">{{ item.partNumber }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">数量</div>
|
||||||
|
<div class="label-value">{{ item.totalCount }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">标重(g)</div>
|
||||||
|
<div class="label-value">{{ item.totalCalculatedWeight }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">包装签字</div>
|
||||||
|
<div class="label-value">{{ item.packingSignature || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">实重(g)</div>
|
||||||
|
<div class="label-value">{{ item.totalWeight || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">检验签字</div>
|
||||||
|
<div class="label-value">{{ item.inspectionSignature || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div class="label">
|
||||||
|
<table class="label-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件名称</div>
|
||||||
|
<div class="label-value">{{ labelData.partName }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">生产日期</div>
|
||||||
|
<div class="label-value">{{ labelData.productionDate }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img v-if="labelData.qrCodeImage" :src="labelData.qrCodeImage" alt="QR Code" />
|
||||||
|
<div class="mark-number">{{ labelData.mark || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件号</div>
|
||||||
|
<div class="label-value">{{ labelData.partNumber }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">数量</div>
|
||||||
|
<div class="label-value">{{ labelData.totalCount }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">标重(g)</div>
|
||||||
|
<div class="label-value">{{ labelData.totalCalculatedWeight }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">包装签字</div>
|
||||||
|
<div class="label-value">{{ labelData.packingSignature || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">实重(g)</div>
|
||||||
|
<div class="label-value">{{ labelData.totalWeight || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">检验签字</div>
|
||||||
|
<div class="label-value">{{ labelData.inspectionSignature || '' }}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="label-actions">
|
||||||
|
<a-button type="primary" @click="printLabel">打印标签</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, nextTick, onMounted, watch } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { getWorkOrder, type WorkOrderInfoResp } from "@/apis/workOrder/workOrder"
|
||||||
|
import QRCode from 'qrcode';
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
previous: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
interface LabelPrintProps {
|
||||||
|
workerOrderId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<LabelPrintProps>(), {
|
||||||
|
workerOrderId: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
workerOrderId: '',
|
||||||
|
encoding: '',
|
||||||
|
materialName: '',
|
||||||
|
orderNo: '',
|
||||||
|
totalCalculatedWeight: '',
|
||||||
|
totalWeight: '',
|
||||||
|
totalCount: '',
|
||||||
|
batch: '',
|
||||||
|
mark: '',
|
||||||
|
qrCodeData: '',
|
||||||
|
workOrderInfos: Array<WorkOrderInfoResp>(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const labelDataList = reactive<Array<{
|
||||||
|
partName: string
|
||||||
|
partNumber: string
|
||||||
|
totalCalculatedWeight: string
|
||||||
|
totalWeight: string
|
||||||
|
productionDate: string
|
||||||
|
totalCount: string
|
||||||
|
packingSignature: string
|
||||||
|
inspectionSignature: string
|
||||||
|
qrCodeData: string
|
||||||
|
qrCodeImage: string
|
||||||
|
mark: string
|
||||||
|
}>>([])
|
||||||
|
|
||||||
|
const labelData = reactive({
|
||||||
|
partName: '',
|
||||||
|
partNumber: '',
|
||||||
|
totalCalculatedWeight: '',
|
||||||
|
totalWeight: '',
|
||||||
|
productionDate: '',
|
||||||
|
totalCount: '',
|
||||||
|
packingSignature: '',
|
||||||
|
inspectionSignature: '',
|
||||||
|
qrCodeData: '',
|
||||||
|
qrCodeImage: '',
|
||||||
|
mark: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const labelContainer = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
|
const handlePrevious = () => {
|
||||||
|
emit('previous')
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchWorkOrderData = async (workerOrderId: string) => {
|
||||||
|
if (!workerOrderId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getWorkOrder(workerOrderId)
|
||||||
|
if (res.code == '0') {
|
||||||
|
formData.encoding = res.data.encoding
|
||||||
|
formData.materialName = res.data.materialName
|
||||||
|
formData.orderNo = res.data.orderNo
|
||||||
|
formData.batch = res.data.batch
|
||||||
|
formData.totalCalculatedWeight = res.data.totalCalculatedWeight
|
||||||
|
formData.totalWeight = res.data.totalWeight
|
||||||
|
formData.totalCount = res.data.totalCount
|
||||||
|
formData.workOrderInfos = res.data.workOrderInfos
|
||||||
|
formData.mark = res.data.mark
|
||||||
|
} else {
|
||||||
|
Message.error('获取工单详情失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取工单详情失败:', error)
|
||||||
|
Message.error('获取工单详情失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateQRCode = async (data: string) => {
|
||||||
|
try {
|
||||||
|
return await QRCode.toDataURL(data, {
|
||||||
|
width: 120,
|
||||||
|
margin: 1,
|
||||||
|
color: {
|
||||||
|
dark: '#000000',
|
||||||
|
light: '#FFFFFF'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成二维码失败:', error)
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateDetailLabel = async () => {
|
||||||
|
if (!formData.workOrderInfos || formData.workOrderInfos.length === 0) {
|
||||||
|
Message.error('未获取到工单明细信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.batch) {
|
||||||
|
Message.error('未获取到批次信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.materialName) {
|
||||||
|
Message.error('未获取到物料信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
labelDataList.length = 0
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const formattedDate = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0') +
|
||||||
|
String(now.getHours()).padStart(2, '0') +
|
||||||
|
String(now.getMinutes()).padStart(2, '0')
|
||||||
|
|
||||||
|
const formattedDate2 = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
for (const workOrderInfo of formData.workOrderInfos) {
|
||||||
|
const orderNo = formData.orderNo + workOrderInfo.id;
|
||||||
|
const qrCodeData = `10#${formData.encoding}$11#9DP$12#${formData.batch}$17#${workOrderInfo.quantity}$20#${formattedDate2}$31#${orderNo}$DY`
|
||||||
|
|
||||||
|
const qrCodeImage = await generateQRCode(qrCodeData)
|
||||||
|
console.log("========", workOrderInfo.mark);
|
||||||
|
|
||||||
|
labelDataList.push({
|
||||||
|
partName: formData.materialName || '',
|
||||||
|
partNumber: formData.encoding || '',
|
||||||
|
totalCalculatedWeight: workOrderInfo.calculatedWeight || '',
|
||||||
|
totalWeight: workOrderInfo.weight || '',
|
||||||
|
productionDate: formattedDate,
|
||||||
|
totalCount: workOrderInfo.quantity || '',
|
||||||
|
packingSignature: '',
|
||||||
|
inspectionSignature: '',
|
||||||
|
qrCodeData: qrCodeData,
|
||||||
|
qrCodeImage: qrCodeImage,
|
||||||
|
mark: workOrderInfo.mark || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Message.success(`成功生成 ${labelDataList.length} 个明细标签`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成明细标签失败:', error)
|
||||||
|
Message.error('生成明细标签失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateOverallLabel = async () => {
|
||||||
|
if (!formData.batch) {
|
||||||
|
Message.error('未获取到批次信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!formData.materialName) {
|
||||||
|
Message.error('未获取到物料信息')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
labelDataList.length = 0
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const formattedDate = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0') +
|
||||||
|
String(now.getHours()).padStart(2, '0') +
|
||||||
|
String(now.getMinutes()).padStart(2, '0')
|
||||||
|
|
||||||
|
const formattedDate2 = now.getFullYear().toString() +
|
||||||
|
String(now.getMonth() + 1).padStart(2, '0') +
|
||||||
|
String(now.getDate()).padStart(2, '0')
|
||||||
|
|
||||||
|
const qrCodeData = `10#${formData.encoding}$11#9DP$12#${formData.batch}$17#${formData.totalCount}$20#${formattedDate2}$31#${formData.orderNo}$DY`
|
||||||
|
|
||||||
|
const qrCodeImage = await generateQRCode(qrCodeData)
|
||||||
|
|
||||||
|
Object.assign(labelData, {
|
||||||
|
partName: formData.materialName || '',
|
||||||
|
partNumber: formData.encoding || '',
|
||||||
|
totalCalculatedWeight: formData.totalCalculatedWeight || '',
|
||||||
|
totalWeight: formData.totalWeight || '',
|
||||||
|
productionDate: formattedDate,
|
||||||
|
totalCount: formData.totalCount || '',
|
||||||
|
packingSignature: '',
|
||||||
|
inspectionSignature: '',
|
||||||
|
qrCodeData: qrCodeData,
|
||||||
|
qrCodeImage: qrCodeImage,
|
||||||
|
mark: formData.mark || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
Message.success('标签生成成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成标签失败:', error)
|
||||||
|
Message.error('生成标签失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const printLabel = async () => {
|
||||||
|
await nextTick()
|
||||||
|
if (labelContainer.value) {
|
||||||
|
const printWindow = window.open('', '_blank')
|
||||||
|
if (printWindow) {
|
||||||
|
const isDetailLabels = labelDataList.length > 0
|
||||||
|
const labelsToPrint = isDetailLabels ? labelDataList : [labelData]
|
||||||
|
|
||||||
|
const labelsHTML = labelsToPrint.map(item => `
|
||||||
|
<div class="label">
|
||||||
|
<table class="label-table">
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件名称</div>
|
||||||
|
<div class="label-value">${item.partName}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">生产日期</div>
|
||||||
|
<div class="label-value">${item.productionDate}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell qr-cell" rowspan="4">
|
||||||
|
<div class="qr-code">
|
||||||
|
<img src="${item.qrCodeImage}" alt="QR Code" />
|
||||||
|
<div class="mark-number">${item.mark || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">零件号</div>
|
||||||
|
<div class="label-value">${item.partNumber}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">数量</div>
|
||||||
|
<div class="label-value">${item.totalCount}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">标重(g)</div>
|
||||||
|
<div class="label-value">${item.totalCalculatedWeight}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">包装签字</div>
|
||||||
|
<div class="label-value">${item.packingSignature || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">实重(g)</div>
|
||||||
|
<div class="label-value">${item.totalWeight || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="label-cell">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="label-field">检验签字</div>
|
||||||
|
<div class="label-value">${item.inspectionSignature || ''}</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
`).join('')
|
||||||
|
|
||||||
|
const printHTML = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>标签打印</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; padding: 10mm; font-family: Arial, sans-serif; }
|
||||||
|
.label-container { display: flex; flex-wrap: wrap; gap: 10mm; justify-content: center; }
|
||||||
|
.label { width: 90mm; height: 38.5mm; border: 1px solid #000; padding: 0; box-sizing: border-box; page-break-inside: avoid; }
|
||||||
|
.label-table { width: 100%; height: 100%; border-collapse: collapse; }
|
||||||
|
.label-cell { border: 1px solid #000; padding: 1mm; vertical-align: top; }
|
||||||
|
.qr-cell { width: 24mm; text-align: center; vertical-align: middle; border: 1px solid #000; }
|
||||||
|
.label-row { display: flex; align-items: center; }
|
||||||
|
.label-field { font-size: 8pt; font-weight: bold; margin-right: 2mm; min-width: 25pt; }
|
||||||
|
.label-value { font-size: 8pt; font-weight: bold; flex: 1; word-break: break-all; }
|
||||||
|
.qr-code img { width: 20mm; height: 20mm; margin: 1mm 0; }
|
||||||
|
.mark-number { font-size: 8pt; font-weight: bold; margin-top: 1mm; text-align: center; }
|
||||||
|
.serial-number { font-size: 8pt; font-weight: bold; margin-top: 1mm; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="label-container">
|
||||||
|
${labelsHTML}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
printWindow.document.write(printHTML)
|
||||||
|
printWindow.document.close()
|
||||||
|
|
||||||
|
printWindow.onload = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
printWindow.focus()
|
||||||
|
printWindow.print()
|
||||||
|
printWindow.close()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.workerOrderId) {
|
||||||
|
fetchWorkOrderData(props.workerOrderId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.workerOrderId, (newWorkerOrderId) => {
|
||||||
|
if (newWorkerOrderId) {
|
||||||
|
fetchWorkOrderData(newWorkerOrderId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
generateDetailLabel,
|
||||||
|
generateOverallLabel
|
||||||
|
})
|
||||||
|
|
||||||
|
defineOptions({ name: 'LabelPrint' })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.label-print-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
background-color: var(--color-bg-2);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid-item {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mark-number {
|
||||||
|
font-size: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-preview-section {
|
||||||
|
margin-top: 30px;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: var(--color-bg-2);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-preview-section h3 {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 450px;
|
||||||
|
height: 180px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-table {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-cell {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 5px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-cell {
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-field {
|
||||||
|
font-size: 10pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 8px;
|
||||||
|
min-width: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-value {
|
||||||
|
font-size: 10pt;
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
:data="dataList"
|
:data="dataList"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1600 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1800 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:disabled-tools="['size']"
|
:disabled-tools="['size']"
|
||||||
:disabled-column-keys="['name']"
|
:disabled-column-keys="['name']"
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
<a-input-search v-model="queryForm.carNo" placeholder="请输入人员卡号" allow-clear @search="search" />
|
<a-input-search v-model="queryForm.carNo" placeholder="请输入人员卡号" allow-clear @search="search" />
|
||||||
<a-input-search v-model="queryForm.orderNo" placeholder="请输入任务工单号" allow-clear @search="search" />
|
<a-input-search v-model="queryForm.orderNo" placeholder="请输入任务工单号" allow-clear @search="search" />
|
||||||
<a-input-search v-model="queryForm.materialName" placeholder="请输入物料名称" allow-clear @search="search" />
|
<a-input-search v-model="queryForm.materialName" placeholder="请输入物料名称" allow-clear @search="search" />
|
||||||
|
<a-input-search v-model="queryForm.batch" placeholder="请输入批次" allow-clear @search="search" />
|
||||||
<a-input-search v-model="queryForm.encoding" placeholder="请输入物料编码" allow-clear @search="search" />
|
<a-input-search v-model="queryForm.encoding" placeholder="请输入物料编码" allow-clear @search="search" />
|
||||||
<a-date-picker
|
<a-date-picker
|
||||||
v-model="queryForm.startDate"
|
v-model="queryForm.startDate"
|
||||||
@@ -44,11 +45,11 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #toolbar-right>
|
<template #toolbar-right>
|
||||||
<a-button v-permission="['workOrder:record:delete']" type="outline" status="danger" @click="onDelete">
|
<a-button v-permission="['weighManage:workOrder:delete']" type="outline" status="danger" @click="onDelete">
|
||||||
<template #icon><icon-delete /></template>
|
<template #icon><icon-delete /></template>
|
||||||
<template #default>删除</template>
|
<template #default>删除</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-permission="['workOrder:record:export']" @click="onExport">
|
<a-button v-permission="['weighManage:workOrder:export']" @click="onExport">
|
||||||
<template #icon><icon-download /></template>
|
<template #icon><icon-download /></template>
|
||||||
<template #default>导出</template>
|
<template #default>导出</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
@@ -63,12 +64,15 @@
|
|||||||
<template #totalWeight="{ record }">
|
<template #totalWeight="{ record }">
|
||||||
{{ record.totalWeight + 'g' }}
|
{{ record.totalWeight + 'g' }}
|
||||||
</template>
|
</template>
|
||||||
|
<template #totalCalculatedWeight="{ record }">
|
||||||
|
{{ record.totalCalculatedWeight + 'g' }}
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #action="{ record }">
|
<template #action="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-link v-permission="['workOrder:record:detail']" title="详情" @click="onDetail(record)">详情</a-link>
|
<a-link v-permission="['workOrder:record:detail']" title="详情" @click="onDetail(record)">详情</a-link>
|
||||||
<a-link v-permission="['workOrder:record:print']" title="打印" @click="onPrint(record)">打印</a-link>
|
<a-link v-permission="['workOrder:record:print']" title="打印" @click="onPrint(record)">打印</a-link>
|
||||||
<a-link v-permission="['workOrder:record:delete']" status="danger" :title="'删除'" @click="onDeleteOne(record.id)">删除</a-link>
|
<a-link v-permission="['weighManage:workOrder:delete']" status="danger" :title="'删除'" @click="onDeleteOne(record.id)">删除</a-link>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</GiTable>
|
</GiTable>
|
||||||
@@ -85,7 +89,7 @@
|
|||||||
row-key="id"
|
row-key="id"
|
||||||
:data="detailData"
|
:data="detailData"
|
||||||
:columns="detailColumns"
|
:columns="detailColumns"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 700 }"
|
:scroll="{ x: '100%', y: '100%', width: 800 }"
|
||||||
>
|
>
|
||||||
<template #imgUrl="{ record }">
|
<template #imgUrl="{ record }">
|
||||||
<a-image :src="record.imgUrl" width="50" />
|
<a-image :src="record.imgUrl" width="50" />
|
||||||
@@ -134,6 +138,7 @@ const queryForm = reactive<WorkOrderQuery>({
|
|||||||
encoding: undefined,
|
encoding: undefined,
|
||||||
userName: undefined,
|
userName: undefined,
|
||||||
carNo: undefined,
|
carNo: undefined,
|
||||||
|
batch: undefined,
|
||||||
startDate: undefined,
|
startDate: undefined,
|
||||||
endDate: undefined,
|
endDate: undefined,
|
||||||
sort: ['w.id,desc']
|
sort: ['w.id,desc']
|
||||||
@@ -167,12 +172,14 @@ const columns = ref<TableInstanceColumns[]>(processColumns([
|
|||||||
{ title: '创建人', dataIndex: 'createUserString' },
|
{ title: '创建人', dataIndex: 'createUserString' },
|
||||||
{ title: '人员卡号', dataIndex: 'cardNo' },
|
{ title: '人员卡号', dataIndex: 'cardNo' },
|
||||||
{ title: '任务工单号', dataIndex: 'orderNo' },
|
{ title: '任务工单号', dataIndex: 'orderNo' },
|
||||||
|
{ title: '批次', dataIndex: 'batch' },
|
||||||
{ title: '物料图片', dataIndex: 'photoUrl', slotName: 'photoUrl', minWidth: 180, ellipsis: true, tooltip: true },
|
{ title: '物料图片', dataIndex: 'photoUrl', slotName: 'photoUrl', minWidth: 180, ellipsis: true, tooltip: true },
|
||||||
{ title: '物料名称', dataIndex: 'materialName' },
|
{ title: '物料名称', dataIndex: 'materialName' },
|
||||||
{ title: '物料编码', dataIndex: 'encoding' },
|
{ title: '物料编码', dataIndex: 'encoding' },
|
||||||
{ title: '单位克重', dataIndex: 'unitWeight' ,slotName: 'unitWeight'},
|
{ title: '单位克重', dataIndex: 'unitWeight' ,slotName: 'unitWeight'},
|
||||||
{ title: '总重量', dataIndex: 'totalWeight' ,slotName: 'totalWeight'},
|
|
||||||
{ title: '总数量', dataIndex: 'totalCount' },
|
{ title: '总数量', dataIndex: 'totalCount' },
|
||||||
|
{ title: '标准总重量', dataIndex: 'totalCalculatedWeight' ,slotName: 'totalCalculatedWeight'},
|
||||||
|
{ title: '实际总重量', dataIndex: 'totalWeight' ,slotName: 'totalWeight'},
|
||||||
{ title: '创建时间', dataIndex: 'createTime', width: 180 },
|
{ title: '创建时间', dataIndex: 'createTime', width: 180 },
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
@@ -181,7 +188,7 @@ const columns = ref<TableInstanceColumns[]>(processColumns([
|
|||||||
width: 160,
|
width: 160,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: !isMobile() ? 'right' : undefined,
|
fixed: !isMobile() ? 'right' : undefined,
|
||||||
show: has.hasPermOr(['workOrder:record:detail', 'workOrder:record:update', 'workOrder:record:delete'])
|
show: has.hasPermOr(['workOrder:record:detail', 'workOrder:record:update', 'weighManage:workOrder:delete'])
|
||||||
}
|
}
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -206,9 +213,10 @@ const detailData = ref<any[]>([])
|
|||||||
const detailColumns = ref<TableInstanceColumns[]>([
|
const detailColumns = ref<TableInstanceColumns[]>([
|
||||||
{ title: '称重次数', dataIndex: 'weightTime' },
|
{ title: '称重次数', dataIndex: 'weightTime' },
|
||||||
{ title: '物料名称', dataIndex: 'materialName' },
|
{ title: '物料名称', dataIndex: 'materialName' },
|
||||||
{ title: '数量', dataIndex: 'quantity' },
|
{ title: '输入数量', dataIndex: 'quantity' },
|
||||||
{ title: '重量', dataIndex: 'weight', slotName: 'weight' },
|
{ title: '标准重量(g)', dataIndex: 'calculatedWeight', slotName: 'calculatedWeight' },
|
||||||
{ title: '计算重量', dataIndex: 'calculatedWeight', slotName: 'calculatedWeight' },
|
{ title: '称重数量', dataIndex: 'weightQuantity' },
|
||||||
|
{ title: '称重重量(g)', dataIndex: 'weight', slotName: 'weight' },
|
||||||
{ title: '抓拍图片', dataIndex: 'imgUrl', slotName: 'imgUrl' }
|
{ title: '抓拍图片', dataIndex: 'imgUrl', slotName: 'imgUrl' }
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user