页面修改
This commit is contained in:
parent
5357434d9d
commit
5e20f8868a
227
package-lock.json
generated
227
package-lock.json
generated
@ -23,6 +23,8 @@
|
|||||||
"naive-ui": "^2.37.3",
|
"naive-ui": "^2.37.3",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
|
"seemly": "^0.3.10",
|
||||||
"three": "^0.184.0",
|
"three": "^0.184.0",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.15",
|
||||||
"vue-draggable-plus": "^0.6.1",
|
"vue-draggable-plus": "^0.6.1",
|
||||||
@ -30,6 +32,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.11.5",
|
"@types/node": "^20.11.5",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/three": "^0.184.1",
|
"@types/three": "^0.184.1",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
"electron": "^42.5.0",
|
"electron": "^42.5.0",
|
||||||
@ -1739,6 +1742,16 @@
|
|||||||
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/qrcode": {
|
||||||
|
"version": "1.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz",
|
||||||
|
"integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/raf": {
|
"node_modules/@types/raf": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||||
@ -2043,7 +2056,6 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@ -2053,7 +2065,6 @@
|
|||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
@ -2502,6 +2513,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/canvg": {
|
"node_modules/canvg": {
|
||||||
"version": "3.0.11",
|
"version": "3.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||||
@ -2620,7 +2640,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
@ -2633,7 +2652,6 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
@ -2823,6 +2841,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/decompress-response": {
|
"node_modules/decompress-response": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
||||||
@ -2933,6 +2960,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/dhtmlx-gantt/-/dhtmlx-gantt-9.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/dhtmlx-gantt/-/dhtmlx-gantt-9.1.4.tgz",
|
||||||
"integrity": "sha512-XCNA5QUiuV79Xq1ykNpH9LFNR2IVpDZMqnmBV6dsBeOkHyPMOpkyQ/gqAPCcK2GAvYHoN2nGAMYb2LldCWhMuQ=="
|
"integrity": "sha512-XCNA5QUiuV79Xq1ykNpH9LFNR2IVpDZMqnmBV6dsBeOkHyPMOpkyQ/gqAPCcK2GAvYHoN2nGAMYb2LldCWhMuQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/dijkstrajs": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dingtalk-jsapi": {
|
"node_modules/dingtalk-jsapi": {
|
||||||
"version": "3.2.9",
|
"version": "3.2.9",
|
||||||
"resolved": "https://registry.npmjs.org/dingtalk-jsapi/-/dingtalk-jsapi-3.2.9.tgz",
|
"resolved": "https://registry.npmjs.org/dingtalk-jsapi/-/dingtalk-jsapi-3.2.9.tgz",
|
||||||
@ -3242,7 +3275,6 @@
|
|||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
@ -3503,6 +3535,19 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/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/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.11",
|
"version": "1.15.11",
|
||||||
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||||
@ -3589,7 +3634,6 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/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.*"
|
||||||
@ -3980,7 +4024,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@ -4169,6 +4212,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/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/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.23",
|
"version": "4.17.23",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz",
|
||||||
@ -4596,6 +4651,42 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/p-locate/node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/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/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pako": {
|
"node_modules/pako": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz",
|
||||||
@ -4619,6 +4710,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@ -4764,6 +4864,15 @@
|
|||||||
"node": ">=10.4.0"
|
"node": ">=10.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.6",
|
||||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
|
||||||
@ -4925,6 +5034,89 @@
|
|||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/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/cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/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/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"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.npmjs.org/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.npmjs.org/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.npmjs.org/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/quick-lru": {
|
"node_modules/quick-lru": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||||
@ -5002,7 +5194,6 @@
|
|||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/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"
|
||||||
@ -5018,6 +5209,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/resedit": {
|
"node_modules/resedit": {
|
||||||
"version": "1.7.2",
|
"version": "1.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz",
|
||||||
@ -5247,6 +5444,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@ -5362,7 +5565,6 @@
|
|||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"emoji-regex": "^8.0.0",
|
"emoji-regex": "^8.0.0",
|
||||||
@ -5377,7 +5579,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ansi-regex": "^5.0.1"
|
"ansi-regex": "^5.0.1"
|
||||||
@ -5922,6 +6123,12 @@
|
|||||||
"node": "^18.17.0 || >=20.5.0"
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-module": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"naive-ui": "^2.37.3",
|
"naive-ui": "^2.37.3",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"seemly": "^0.3.10",
|
"seemly": "^0.3.10",
|
||||||
"three": "^0.184.0",
|
"three": "^0.184.0",
|
||||||
"vue": "^3.4.15",
|
"vue": "^3.4.15",
|
||||||
@ -35,6 +36,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.11.5",
|
"@types/node": "^20.11.5",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/three": "^0.184.1",
|
"@types/three": "^0.184.1",
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
"electron": "^42.5.0",
|
"electron": "^42.5.0",
|
||||||
|
|||||||
@ -16,6 +16,10 @@ export interface AssingWorkDetail {
|
|||||||
|
|
||||||
createTime?: string
|
createTime?: string
|
||||||
|
|
||||||
|
pauseSeconds?: number
|
||||||
|
shuntSeconds?: number
|
||||||
|
actualWorkSeconds?: number
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssingWorkDetail API
|
// AssingWorkDetail API
|
||||||
|
|||||||
@ -349,6 +349,20 @@ export const orderProcessPlanApi = {
|
|||||||
|
|
||||||
orderItemId?: number | string
|
orderItemId?: number | string
|
||||||
|
|
||||||
|
/** 物料名称 */
|
||||||
|
materialName?: string
|
||||||
|
|
||||||
|
orderItemName?: string
|
||||||
|
|
||||||
|
/** 生产令号 */
|
||||||
|
mainCode?: string
|
||||||
|
|
||||||
|
/** 物料编码 */
|
||||||
|
materialCode?: string
|
||||||
|
|
||||||
|
/** 车间 id */
|
||||||
|
workshopId?: number | string | null
|
||||||
|
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
return request<{ list: OrderProcessPlanVO[]; total: number; page: number; pageSize: number }>({
|
return request<{ list: OrderProcessPlanVO[]; total: number; page: number; pageSize: number }>({
|
||||||
|
|||||||
@ -30,6 +30,21 @@ export interface Outsourcing {
|
|||||||
issuedQuantity?: number | string
|
issuedQuantity?: number | string
|
||||||
receivedQuantity?: number | string
|
receivedQuantity?: number | string
|
||||||
qualifiedQuantity?: number | string
|
qualifiedQuantity?: number | string
|
||||||
|
/** 0未下推 1已下推 */
|
||||||
|
kingdeePushed?: number
|
||||||
|
kingdeeBillNo?: string
|
||||||
|
kingdeePushTime?: string
|
||||||
|
kingdeePushBy?: string
|
||||||
|
details?: OutsourcingDetail[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OutsourcingDetail {
|
||||||
|
id?: number
|
||||||
|
outsourcingId?: number
|
||||||
|
flowingAroundId?: number
|
||||||
|
quantity?: number
|
||||||
|
remark?: string
|
||||||
|
createTime?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OUTSOURCE_STATUS_MAP: Record<number, { label: string; type: 'default' | 'info' | 'success' | 'warning' | 'error' }> = {
|
export const OUTSOURCE_STATUS_MAP: Record<number, { label: string; type: 'default' | 'info' | 'success' | 'warning' | 'error' }> = {
|
||||||
@ -67,6 +82,14 @@ export const outsourcingApi = {
|
|||||||
return request<Outsourcing>({ url: `/biz/outsourcing/${id}`, method: 'get' })
|
return request<Outsourcing>({ url: `/biz/outsourcing/${id}`, method: 'get' })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
details(id: number | string) {
|
||||||
|
return request<OutsourcingDetail[]>({ url: `/biz/outsourcing/${id}/details`, method: 'get' })
|
||||||
|
},
|
||||||
|
|
||||||
|
pushKingdee(id: number | string) {
|
||||||
|
return request({ url: `/biz/outsourcing/${id}/pushKingdee`, method: 'post' })
|
||||||
|
},
|
||||||
|
|
||||||
create(data: Outsourcing) {
|
create(data: Outsourcing) {
|
||||||
return request({ url: '/biz/outsourcing', method: 'post', data })
|
return request({ url: '/biz/outsourcing', method: 'post', data })
|
||||||
},
|
},
|
||||||
|
|||||||
@ -16,6 +16,10 @@ export interface AssingWork {
|
|||||||
status:number,
|
status:number,
|
||||||
reason:string,
|
reason:string,
|
||||||
pauseTime:Date,
|
pauseTime:Date,
|
||||||
|
pauseSeconds?: number,
|
||||||
|
shuntSeconds?: number,
|
||||||
|
actualWorkSeconds?: number,
|
||||||
|
unitWorkSeconds?: number,
|
||||||
assingStatus:number,
|
assingStatus:number,
|
||||||
assingCode:string,
|
assingCode:string,
|
||||||
parentId:number,
|
parentId:number,
|
||||||
|
|||||||
@ -24,6 +24,10 @@ export interface SubmitLog {
|
|||||||
|
|
||||||
remark?: string
|
remark?: string
|
||||||
|
|
||||||
|
pauseSeconds?: number
|
||||||
|
shuntSeconds?: number
|
||||||
|
actualWorkSeconds?: number
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 派工工单汇报记录 API
|
// 派工工单汇报记录 API
|
||||||
|
|||||||
@ -12,4 +12,16 @@ export function getaDtetime(timestamp:any) {
|
|||||||
|
|
||||||
|
|
||||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 工时秒数格式化为 小时/分/秒 */
|
||||||
|
export function formatWorkSeconds(seconds?: number | string | null) {
|
||||||
|
const n = Number(seconds)
|
||||||
|
if (!Number.isFinite(n) || n < 0) return '0秒'
|
||||||
|
const h = Math.floor(n / 3600)
|
||||||
|
const m = Math.floor((n % 3600) / 60)
|
||||||
|
const s = Math.floor(n % 60)
|
||||||
|
if (h > 0) return `${h}小时${m}分${s}秒`
|
||||||
|
if (m > 0) return `${m}分${s}秒`
|
||||||
|
return `${s}秒`
|
||||||
}
|
}
|
||||||
@ -1,36 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="plan-board-page">
|
<div class="page-container plan-board-page">
|
||||||
<!-- 顶部工具栏 -->
|
<!-- 顶部工具栏 -->
|
||||||
<div class="board-toolbar">
|
<div class="board-toolbar">
|
||||||
<n-space :size="8" align="center">
|
<n-space :size="10" align="center">
|
||||||
<n-button type="primary" size="small" @click="handleAdd">
|
<n-button type="primary" @click="handleAdd">
|
||||||
<template #icon><n-icon><AddOutline /></n-icon></template>
|
<template #icon><n-icon><AddOutline /></n-icon></template>
|
||||||
新增工序
|
新增工序
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button size="small" type="success" ghost disabled title="待后端接口支持">
|
<n-button type="success" ghost disabled title="待后端接口支持">
|
||||||
<template #icon><n-icon><CheckmarkCircleOutline /></n-icon></template>
|
<template #icon><n-icon><CheckmarkCircleOutline /></n-icon></template>
|
||||||
齐套自动下达
|
齐套自动下达
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button size="small" type="error" ghost disabled title="待后端接口支持">批量删除</n-button>
|
<n-button type="error" ghost disabled title="待后端接口支持">批量删除</n-button>
|
||||||
<n-button size="small" type="primary" ghost disabled title="待后端接口支持">批量下达</n-button>
|
<n-button type="primary" ghost disabled title="待后端接口支持">批量下达</n-button>
|
||||||
<n-button size="small" quaternary disabled title="待后端接口支持">合并为组工单</n-button>
|
<n-button quaternary disabled title="待后端接口支持">合并为组工单</n-button>
|
||||||
<n-dropdown :options="batchOptions" trigger="click" @select="onBatchSelect">
|
<n-dropdown :options="batchOptions" trigger="click" @select="onBatchSelect">
|
||||||
<n-button size="small" quaternary>
|
<n-button quaternary>
|
||||||
批量操作
|
批量操作
|
||||||
<n-icon style="margin-left: 4px"><ChevronDownOutline /></n-icon>
|
<n-icon style="margin-left: 4px"><ChevronDownOutline /></n-icon>
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</n-space>
|
</n-space>
|
||||||
<n-space :size="8" align="center">
|
<n-space :size="10" align="center">
|
||||||
<n-button size="small" quaternary @click="importModalVisible = true">
|
<n-button quaternary @click="importModalVisible = true">
|
||||||
<template #icon><n-icon><CloudUploadOutline /></n-icon></template>
|
<template #icon><n-icon><CloudUploadOutline /></n-icon></template>
|
||||||
导入
|
导入
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button size="small" quaternary @click="handleExport">
|
<n-button quaternary @click="handleExport">
|
||||||
<template #icon><n-icon><DownloadOutline /></n-icon></template>
|
<template #icon><n-icon><DownloadOutline /></n-icon></template>
|
||||||
导出
|
导出
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button size="small" quaternary @click="loadData">
|
<n-button quaternary @click="loadData">
|
||||||
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
||||||
同步
|
同步
|
||||||
</n-button>
|
</n-button>
|
||||||
@ -39,20 +39,36 @@
|
|||||||
|
|
||||||
<!-- 筛选 -->
|
<!-- 筛选 -->
|
||||||
<div class="board-filter">
|
<div class="board-filter">
|
||||||
<n-form inline :model="searchForm" label-placement="left" size="small">
|
<n-form inline :model="searchForm" label-placement="left" label-width="72">
|
||||||
<n-form-item label="工序名称">
|
<n-form-item label="生产令号">
|
||||||
<n-input v-model:value="searchForm.name" placeholder="工序名称" clearable style="width: 140px" />
|
<n-input v-model:value="searchForm.mainCode" placeholder="生产令号" clearable style="width: 150px" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="工序编码">
|
<n-form-item label="物料编码">
|
||||||
<n-input v-model:value="searchForm.code" placeholder="工序编码" clearable style="width: 140px" />
|
<n-input v-model:value="searchForm.materialCode" placeholder="物料编码" clearable style="width: 150px" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="物料名称">
|
||||||
|
<n-input v-model:value="searchForm.materialName" placeholder="物料名称" clearable style="width: 150px" />
|
||||||
|
</n-form-item>
|
||||||
|
<n-form-item label="车间">
|
||||||
|
<n-select
|
||||||
|
v-model:value="searchForm.workshopId"
|
||||||
|
:options="workshopOptions"
|
||||||
|
placeholder="请选择车间"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
style="width: 150px"
|
||||||
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item>
|
<n-form-item>
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-button type="primary" size="small" @click="handleSearch">
|
<n-button type="primary" @click="handleSearch">
|
||||||
<template #icon><n-icon><SearchOutline /></n-icon></template>
|
<template #icon><n-icon><SearchOutline /></n-icon></template>
|
||||||
搜索
|
搜索
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button size="small" @click="handleReset">重置</n-button>
|
<n-button @click="handleReset">
|
||||||
|
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
||||||
|
重置
|
||||||
|
</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
@ -62,7 +78,8 @@
|
|||||||
<!-- 表头 -->
|
<!-- 表头 -->
|
||||||
<div class="board-table-head">
|
<div class="board-table-head">
|
||||||
<div class="col-check" />
|
<div class="col-check" />
|
||||||
<div class="col-product">产品 / 工单编号</div>
|
<div class="col-main-code">生产令号</div>
|
||||||
|
<div class="col-product">物料名称 / 编码</div>
|
||||||
<div class="col-qty">数量</div>
|
<div class="col-qty">数量</div>
|
||||||
<div class="col-mode">模式</div>
|
<div class="col-mode">模式</div>
|
||||||
<div class="col-kit">齐套率</div>
|
<div class="col-kit">齐套率</div>
|
||||||
@ -86,18 +103,26 @@
|
|||||||
@update:checked="(v: boolean) => toggleSelect(orderRowKey(order), v)"
|
@update:checked="(v: boolean) => toggleSelect(orderRowKey(order), v)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-main-code" :title="order.mainCode || ''">
|
||||||
|
{{ order.mainCode || '-' }}
|
||||||
|
</div>
|
||||||
<div class="col-product">
|
<div class="col-product">
|
||||||
<div class="product-name">{{ order.materialName || '-' }}</div>
|
<div class="product-name" :title="order.materialName || ''">
|
||||||
<div class="product-code">
|
{{ order.materialName || '-' }}
|
||||||
|
</div>
|
||||||
|
<div class="product-code" :title="order.materialCode || ''">
|
||||||
|
{{ order.materialCode || '-' }}
|
||||||
|
</div>
|
||||||
|
<div class="product-sub">
|
||||||
<span>{{ formatWorkOrderCodes(order) }}</span>
|
<span>{{ formatWorkOrderCodes(order) }}</span>
|
||||||
<n-tag v-if="isOverdue(order)" size="tiny" type="error" :bordered="false">逾期</n-tag>
|
<n-tag v-if="isOverdue(order)" size="small" type="error" :bordered="false">逾期</n-tag>
|
||||||
<n-tag v-if="hasOutsource(order)" size="tiny" type="warning" :bordered="false">委外</n-tag>
|
<n-tag v-if="hasOutsource(order)" size="small" type="warning" :bordered="false">委外</n-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-qty">{{ order.quantity ?? '-' }}</div>
|
<div class="col-qty">{{ order.quantity ?? '-' }}</div>
|
||||||
<div class="col-mode">
|
<div class="col-mode">
|
||||||
<n-tag v-if="order.routeCode" size="tiny" :bordered="false">工艺型</n-tag>
|
<n-tag v-if="order.routeCode" size="small" :bordered="false">工艺型</n-tag>
|
||||||
<n-tag v-if="order.workshopName" size="tiny" type="info" :bordered="false">{{ order.workshopName }}</n-tag>
|
<n-tag v-if="order.workshopName" size="small" type="info" :bordered="false">{{ order.workshopName }}</n-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-kit">-</div>
|
<div class="col-kit">-</div>
|
||||||
<div class="col-flow" :class="{ 'flow-expanded': isExpanded(order) }">
|
<div class="col-flow" :class="{ 'flow-expanded': isExpanded(order) }">
|
||||||
@ -109,29 +134,29 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-plan">
|
<div class="col-plan">
|
||||||
<div>{{ formatShortTime(order.beginTime) }}</div>
|
<div>{{ formatShortTime(order.beginTime) }}</div>
|
||||||
<div class="sub-text">{{ formatShortTime(order.endTime) }}</div>
|
<div>{{ formatShortTime(order.endTime) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-life">
|
<div class="col-life">
|
||||||
<div class="life-cell">
|
<div class="life-cell">
|
||||||
<n-icon :color="lifeIcon(order).color" size="18">
|
<n-icon :color="lifeIcon(order).color" size="20">
|
||||||
<component :is="lifeIcon(order).icon" />
|
<component :is="lifeIcon(order).icon" />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
<span>{{ lifeLabel(order) }}</span>
|
<span>{{ lifeLabel(order) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-actions">
|
<div class="col-actions">
|
||||||
<n-button text type="primary" size="tiny" @click="openDetail(order)">详情</n-button>
|
<n-button text type="primary" size="small" @click="openDetail(order)">详情</n-button>
|
||||||
<n-button text type="info" size="tiny" @click="openPlmModel(order)">模型</n-button>
|
<n-button text type="info" size="small" @click="openPlmModel(order)">模型</n-button>
|
||||||
<!-- 订单级编辑:打开工序顺序重排弹窗(不进单工序编辑) -->
|
<!-- 订单级编辑:打开工序顺序重排弹窗(不进单工序编辑) -->
|
||||||
<n-button
|
<n-button
|
||||||
text
|
text
|
||||||
type="primary"
|
type="primary"
|
||||||
size="tiny"
|
size="small"
|
||||||
:disabled="!hasPermission('biz:orderProcessPlan:edit')"
|
:disabled="!hasPermission('biz:orderProcessPlan:edit')"
|
||||||
@click="openReorder(order)"
|
@click="openReorder(order)"
|
||||||
>编辑</n-button>
|
>编辑</n-button>
|
||||||
<n-dropdown :options="moreOptions" trigger="click" @select="(k) => onMoreSelect(k, order)">
|
<n-dropdown :options="moreOptions" trigger="click" @select="(k) => onMoreSelect(k, order)">
|
||||||
<n-button text size="tiny">更多</n-button>
|
<n-button text size="small">更多</n-button>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -507,7 +532,7 @@
|
|||||||
label-width="110"
|
label-width="110"
|
||||||
>
|
>
|
||||||
<n-form-item label="供应商" path="supplierName">
|
<n-form-item label="供应商" path="supplierName">
|
||||||
<n-input v-model:value="outsourceForm.supplierName" placeholder="请输入供应商名称" />
|
<n-input v-model:value="outsourceForm.supplierName" placeholder="可不填,空则为草稿,补齐后再下推金蝶" />
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="委外数量" path="quantity">
|
<n-form-item label="委外数量" path="quantity">
|
||||||
<n-input-number
|
<n-input-number
|
||||||
@ -675,6 +700,11 @@
|
|||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
|
<WorkOrderPrintPreview
|
||||||
|
v-model:show="printVisible"
|
||||||
|
:order="printData"
|
||||||
|
:loading="printLoading"
|
||||||
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -684,7 +714,8 @@ import {ref, reactive, onMounted, h, computed, type Component, toRaw} from 'vue'
|
|||||||
import {
|
import {
|
||||||
NButton, NSpace, NIcon, NTag, NSpin, NModal, NForm, NFormItem, NGrid, NGi,
|
NButton, NSpace, NIcon, NTag, NSpin, NModal, NForm, NFormItem, NGrid, NGi,
|
||||||
NInput, NInputNumber, NDatePicker, NCheckbox, NEmpty, NPagination, NDropdown, NDescriptions,
|
NInput, NInputNumber, NDatePicker, NCheckbox, NEmpty, NPagination, NDropdown, NDescriptions,
|
||||||
NDescriptionsItem, NAlert, NUpload, NText, NCard, NRadioGroup, NRadio, NDivider, useMessage, useDialog,
|
NDescriptionsItem, NAlert, NUpload, NText, NCard, NRadioGroup, NRadio, NDivider, NSelect,
|
||||||
|
useMessage, useDialog,
|
||||||
type UploadCustomRequestOptions, type FormRules,
|
type UploadCustomRequestOptions, type FormRules,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import {
|
import {
|
||||||
@ -706,9 +737,11 @@ import {
|
|||||||
type OutsourcingProcessOption, OrderProcessPlan,
|
type OutsourcingProcessOption, OrderProcessPlan,
|
||||||
} from '@/api/orderProcessPlan'
|
} from '@/api/orderProcessPlan'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import { deptApi } from '@/api/org'
|
||||||
import ProcessFlowStepper from './components/ProcessFlowStepper.vue'
|
import ProcessFlowStepper from './components/ProcessFlowStepper.vue'
|
||||||
import ProcessCard from './components/ProcessCard.vue'
|
import ProcessCard from './components/ProcessCard.vue'
|
||||||
import SplitProcessDialog from './components/SplitProcessDialog.vue'
|
import SplitProcessDialog from './components/SplitProcessDialog.vue'
|
||||||
|
import WorkOrderPrintPreview from './components/WorkOrderPrintPreview.vue'
|
||||||
import PlmModelDrawer from '@/components/PlmModelDrawer.vue'
|
import PlmModelDrawer from '@/components/PlmModelDrawer.vue'
|
||||||
import type { PlmModelOpenContext } from '@/api/plmModel'
|
import type { PlmModelOpenContext } from '@/api/plmModel'
|
||||||
import Pgitem from './pgitem.vue'
|
import Pgitem from './pgitem.vue'
|
||||||
@ -723,10 +756,14 @@ const userStore = useUserStore()
|
|||||||
const hasPermission = (p: string) => userStore.hasPermission(p)
|
const hasPermission = (p: string) => userStore.hasPermission(p)
|
||||||
|
|
||||||
const searchForm = reactive({
|
const searchForm = reactive({
|
||||||
name: '',
|
mainCode: '',
|
||||||
code: '',
|
materialCode: '',
|
||||||
|
materialName: '',
|
||||||
|
workshopId: null as number | null,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const workshopOptions = ref<{ label: string; value: number }[]>([])
|
||||||
|
|
||||||
const tableData = ref<OrderProcessPlanVO[]>([])
|
const tableData = ref<OrderProcessPlanVO[]>([])
|
||||||
const expandedIds = ref<Set<string>>(new Set())
|
const expandedIds = ref<Set<string>>(new Set())
|
||||||
const selectedOrderIds = ref<string[]>([])
|
const selectedOrderIds = ref<string[]>([])
|
||||||
@ -737,6 +774,10 @@ const detailVisible = ref(false)
|
|||||||
const detailLoading = ref(false)
|
const detailLoading = ref(false)
|
||||||
const detailData = ref<OrderProcessPlanVO | null>(null)
|
const detailData = ref<OrderProcessPlanVO | null>(null)
|
||||||
|
|
||||||
|
const printVisible = ref(false)
|
||||||
|
const printLoading = ref(false)
|
||||||
|
const printData = ref<OrderProcessPlanVO | null>(null)
|
||||||
|
|
||||||
/** 工序工单拆单 */
|
/** 工序工单拆单 */
|
||||||
const splitVisible = ref(false)
|
const splitVisible = ref(false)
|
||||||
const splitPlanId = ref<number | null>(null)
|
const splitPlanId = ref<number | null>(null)
|
||||||
@ -793,7 +834,6 @@ const outsourceForm = reactive({
|
|||||||
remark: '',
|
remark: '',
|
||||||
})
|
})
|
||||||
const outsourceRules: FormRules = {
|
const outsourceRules: FormRules = {
|
||||||
supplierName: [{ required: true, message: '请输入供应商', trigger: 'blur' }],
|
|
||||||
quantity: [{ required: true, type: 'number', message: '请输入委外数量', trigger: ['blur', 'change'] }],
|
quantity: [{ required: true, type: 'number', message: '请输入委外数量', trigger: ['blur', 'change'] }],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,8 +1071,10 @@ async function loadData() {
|
|||||||
const res = await orderProcessPlanApi.page({
|
const res = await orderProcessPlanApi.page({
|
||||||
page: pagination.page,
|
page: pagination.page,
|
||||||
pageSize: pagination.pageSize,
|
pageSize: pagination.pageSize,
|
||||||
name: searchForm.name || undefined,
|
mainCode: searchForm.mainCode || undefined,
|
||||||
code: searchForm.code || undefined,
|
materialCode: searchForm.materialCode || undefined,
|
||||||
|
materialName: searchForm.materialName || undefined,
|
||||||
|
workshopId: searchForm.workshopId ?? undefined,
|
||||||
})
|
})
|
||||||
tableData.value = res.list ?? []
|
tableData.value = res.list ?? []
|
||||||
pagination.itemCount = res.total
|
pagination.itemCount = res.total
|
||||||
@ -1047,8 +1089,10 @@ function handleSearch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleReset() {
|
function handleReset() {
|
||||||
searchForm.name = ''
|
searchForm.mainCode = ''
|
||||||
searchForm.code = ''
|
searchForm.materialCode = ''
|
||||||
|
searchForm.materialName = ''
|
||||||
|
searchForm.workshopId = null
|
||||||
handleSearch()
|
handleSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,6 +1116,10 @@ function onBatchSelect(key: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onMoreSelect(key: string, order: OrderProcessPlanVO) {
|
function onMoreSelect(key: string, order: OrderProcessPlanVO) {
|
||||||
|
if (key === 'print') {
|
||||||
|
openPrintPreview(order)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (key === 'createOutsource') {
|
if (key === 'createOutsource') {
|
||||||
openOutsource(order)
|
openOutsource(order)
|
||||||
return
|
return
|
||||||
@ -1086,7 +1134,6 @@ function onMoreSelect(key: string, order: OrderProcessPlanVO) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const labels: Record<string, string> = {
|
const labels: Record<string, string> = {
|
||||||
print: '打印',
|
|
||||||
withdraw: '撤回',
|
withdraw: '撤回',
|
||||||
freeze: '冻结工单',
|
freeze: '冻结工单',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
@ -1181,14 +1228,14 @@ async function submitOutsource() {
|
|||||||
try {
|
try {
|
||||||
await orderProcessPlanApi.createOutsourcing({
|
await orderProcessPlanApi.createOutsourcing({
|
||||||
processPlanId: outsourceSelectedPlanId.value,
|
processPlanId: outsourceSelectedPlanId.value,
|
||||||
supplierName: outsourceForm.supplierName.trim(),
|
supplierName: outsourceForm.supplierName.trim() || undefined,
|
||||||
quantity: outsourceForm.quantity!,
|
quantity: outsourceForm.quantity!,
|
||||||
unitPrice: outsourceForm.unitPrice ?? undefined,
|
unitPrice: outsourceForm.unitPrice ?? undefined,
|
||||||
planStartTime: formatSubmitDateTime(outsourceForm.planStartTime),
|
planStartTime: formatSubmitDateTime(outsourceForm.planStartTime),
|
||||||
planEndTime: formatSubmitDateTime(outsourceForm.planEndTime),
|
planEndTime: formatSubmitDateTime(outsourceForm.planEndTime),
|
||||||
remark: outsourceForm.remark || undefined,
|
remark: outsourceForm.remark || undefined,
|
||||||
})
|
})
|
||||||
message.success('委外单创建成功')
|
message.success(outsourceForm.supplierName.trim() ? '委外单创建成功' : '已创建草稿委外单,补齐供应商后可下推金蝶')
|
||||||
outsourceModalVisible.value = false
|
outsourceModalVisible.value = false
|
||||||
loadData()
|
loadData()
|
||||||
} finally {
|
} finally {
|
||||||
@ -1196,6 +1243,24 @@ async function submitOutsource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openPrintPreview(order: OrderProcessPlanVO) {
|
||||||
|
if (!order.workOrderCode) {
|
||||||
|
message.warning('当前记录缺少工单号,二维码将留空')
|
||||||
|
}
|
||||||
|
printVisible.value = true
|
||||||
|
printLoading.value = true
|
||||||
|
printData.value = order
|
||||||
|
try {
|
||||||
|
if (order.orderItemId) {
|
||||||
|
printData.value = await orderProcessPlanApi.detail(order.orderItemId, order.workOrderCode)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
printData.value = order
|
||||||
|
} finally {
|
||||||
|
printLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function openDetail(order: OrderProcessPlanVO) {
|
async function openDetail(order: OrderProcessPlanVO) {
|
||||||
if (!order.orderItemId) return
|
if (!order.orderItemId) return
|
||||||
detailVisible.value = true
|
detailVisible.value = true
|
||||||
@ -1404,10 +1469,7 @@ function dispatchSubmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleExport() {
|
async function handleExport() {
|
||||||
const blob = await orderProcessPlanApi.export({
|
const blob = await orderProcessPlanApi.export({})
|
||||||
name: searchForm.name || undefined,
|
|
||||||
code: searchForm.code || undefined,
|
|
||||||
})
|
|
||||||
const url = window.URL.createObjectURL(blob)
|
const url = window.URL.createObjectURL(blob)
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = url
|
link.href = url
|
||||||
@ -1451,12 +1513,26 @@ function openReportDetail(order) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(loadData)
|
async function loadWorkshopOptions() {
|
||||||
|
try {
|
||||||
|
const data = await deptApi.list()
|
||||||
|
workshopOptions.value = (data || [])
|
||||||
|
.filter((d: any) => d.deptName && String(d.deptName).includes('车间'))
|
||||||
|
.map((d: any) => ({ label: d.deptName, value: Number(d.id) }))
|
||||||
|
} catch {
|
||||||
|
workshopOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadWorkshopOptions()
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.plan-board-page {
|
.plan-board-page {
|
||||||
padding: 0;
|
padding: 20px;
|
||||||
background: #f5f7fa;
|
background: #f5f7fa;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
@ -1467,18 +1543,23 @@ onMounted(loadData)
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 12px 16px;
|
padding: 16px 20px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
border-bottom: 1px solid #eef0f3;
|
border-bottom: 1px solid #eef0f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-filter {
|
.board-filter {
|
||||||
padding: 12px 16px;
|
padding: 12px 20px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-bottom: 1px solid #eef0f3;
|
border-bottom: 1px solid #eef0f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-filter :deep(.n-form-item) {
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.board-table-wrap {
|
.board-table-wrap {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -1487,28 +1568,29 @@ onMounted(loadData)
|
|||||||
.board-table-head,
|
.board-table-head,
|
||||||
.order-row {
|
.order-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
/* 勾选 | 产品工单 | 数量 | 模式 | 齐套率 | 工序轴 | 计划时间 | 生命周期 | 操作 */
|
/* 勾选 | 生产令号 | 物料名称/编码 | 数量 | 模式 | 齐套率 | 工序轴 | 计划时间 | 生命周期 | 操作 */
|
||||||
grid-template-columns:
|
grid-template-columns:
|
||||||
40px
|
48px
|
||||||
minmax(220px, 1.6fr)
|
minmax(150px, 1fr)
|
||||||
64px
|
minmax(240px, 1.6fr)
|
||||||
108px
|
|
||||||
72px
|
72px
|
||||||
minmax(200px, 1.4fr)
|
minmax(150px, 1fr)
|
||||||
|
72px
|
||||||
|
minmax(240px, 1.5fr)
|
||||||
168px
|
168px
|
||||||
96px
|
110px
|
||||||
140px;
|
168px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 10px;
|
||||||
padding: 0 16px;
|
padding: 0 20px;
|
||||||
min-width: 1280px;
|
min-width: 1560px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-table-head {
|
.board-table-head {
|
||||||
height: 40px;
|
height: 46px;
|
||||||
background: #fafbfc;
|
background: #fafbfc;
|
||||||
border-bottom: 1px solid #e8eaed;
|
border-bottom: 1px solid #e8eaed;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
}
|
}
|
||||||
@ -1519,7 +1601,7 @@ onMounted(loadData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-row {
|
.order-row {
|
||||||
min-height: 64px;
|
min-height: 84px;
|
||||||
transition: background 0.15s;
|
transition: background 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1544,12 +1626,23 @@ onMounted(loadData)
|
|||||||
.col-qty,
|
.col-qty,
|
||||||
.col-kit {
|
.col-kit {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-name {
|
.col-main-code {
|
||||||
font-size: 14px;
|
text-align: left;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a1a;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-name,
|
||||||
|
.product-code {
|
||||||
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #1a1a1a;
|
color: #1a1a1a;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -1558,31 +1651,40 @@ onMounted(loadData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.product-code {
|
.product-code {
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-sub {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
flex-wrap: nowrap;
|
||||||
|
gap: 8px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
|
line-height: 1.5;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-sub :deep(.n-tag) {
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-mode {
|
.col-mode {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
gap: 4px;
|
flex-wrap: wrap;
|
||||||
align-items: flex-start;
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-plan {
|
.col-plan {
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
line-height: 1.4;
|
line-height: 1.5;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
color: #1a1a1a;
|
||||||
|
|
||||||
.col-plan .sub-text {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #8c8c8c;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-life {
|
.col-life {
|
||||||
@ -1602,19 +1704,19 @@ onMounted(loadData)
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-actions {
|
.col-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 6px;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.order-expand {
|
.order-expand {
|
||||||
padding: 12px 16px 16px 56px;
|
padding: 16px 20px 20px 68px;
|
||||||
background: linear-gradient(180deg, rgba(32, 128, 240, 0.03) 0%, #fff 100%);
|
background: linear-gradient(180deg, rgba(32, 128, 240, 0.03) 0%, #fff 100%);
|
||||||
border-top: 1px dashed #e8eaed;
|
border-top: 1px dashed #e8eaed;
|
||||||
}
|
}
|
||||||
@ -1635,20 +1737,20 @@ onMounted(loadData)
|
|||||||
}
|
}
|
||||||
|
|
||||||
.expand-empty {
|
.expand-empty {
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
padding: 8px 0;
|
padding: 12px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-empty {
|
.board-empty {
|
||||||
padding: 48px;
|
padding: 56px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board-pagination {
|
.board-pagination {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
padding: 12px 16px;
|
padding: 16px 20px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 0 0 8px 8px;
|
border-radius: 0 0 8px 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
:stroke-width="10"
|
:stroke-width="10"
|
||||||
:color="progressColor"
|
:color="progressColor"
|
||||||
:rail-color="railColor"
|
:rail-color="railColor"
|
||||||
style="width: 88px"
|
style="width: 96px"
|
||||||
>
|
>
|
||||||
<span class="progress-text">{{ progressPct }}%</span>
|
<span class="progress-text">{{ progressPct }}%</span>
|
||||||
</n-progress>
|
</n-progress>
|
||||||
@ -74,47 +74,47 @@
|
|||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<n-button
|
<n-button
|
||||||
v-if="canAssign"
|
v-if="canAssign"
|
||||||
size="tiny"
|
size="small"
|
||||||
type="warning"
|
type="warning"
|
||||||
ghost
|
ghost
|
||||||
@click="emit('assign', process)"
|
@click="emit('assign', process)"
|
||||||
>派工</n-button>
|
>派工</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
v-if="canSplit"
|
v-if="canSplit"
|
||||||
size="tiny"
|
size="small"
|
||||||
type="primary"
|
type="primary"
|
||||||
ghost
|
ghost
|
||||||
@click="emit('split', process)"
|
@click="emit('split', process)"
|
||||||
>拆单</n-button>
|
>拆单</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
v-if="canEdit"
|
v-if="canEdit"
|
||||||
size="tiny"
|
size="small"
|
||||||
ghost
|
ghost
|
||||||
@click="emit('edit', process)"
|
@click="emit('edit', process)"
|
||||||
>编辑</n-button>
|
>编辑</n-button>
|
||||||
<n-button size="tiny" type="info" ghost @click="emit('viewModel', process)">模型</n-button>
|
<n-button size="small" type="info" ghost @click="emit('viewModel', process)">模型</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
v-if="canOutsource"
|
v-if="canOutsource"
|
||||||
size="tiny"
|
size="small"
|
||||||
type="warning"
|
type="warning"
|
||||||
ghost
|
ghost
|
||||||
@click="emit('outsource', process)"
|
@click="emit('outsource', process)"
|
||||||
>委外</n-button>
|
>委外</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
size="tiny"
|
size="small"
|
||||||
type="info"
|
type="info"
|
||||||
ghost
|
ghost
|
||||||
@click="emit('submitDetail', process)"
|
@click="emit('submitDetail', process)"
|
||||||
>汇报</n-button>
|
>汇报</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
:disabled="refillDisAbled"
|
:disabled="refillDisAbled"
|
||||||
size="tiny"
|
size="small"
|
||||||
type="info"
|
type="info"
|
||||||
ghost
|
ghost
|
||||||
@click="emit('handleLeftoverMaterial', process)"
|
@click="emit('handleLeftoverMaterial', process)"
|
||||||
>补料</n-button>
|
>补料</n-button>
|
||||||
<n-button
|
<n-button
|
||||||
size="tiny"
|
size="small"
|
||||||
:type="process.processStatus === 4 ? 'success' : 'default'"
|
:type="process.processStatus === 4 ? 'success' : 'default'"
|
||||||
:ghost="process.processStatus !== 4"
|
:ghost="process.processStatus !== 4"
|
||||||
disabled
|
disabled
|
||||||
@ -122,7 +122,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!isLast" class="card-chevron">
|
<div v-if="!isLast" class="card-chevron">
|
||||||
<n-icon size="28" color="#c0c4cc"><ChevronForwardOutline /></n-icon>
|
<n-icon size="30" color="#c0c4cc"><ChevronForwardOutline /></n-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -275,7 +275,7 @@ onMounted(()=>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.process-card {
|
.process-card {
|
||||||
width: 220px;
|
width: 248px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid #e8e8e8;
|
border: 1px solid #e8e8e8;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -323,12 +323,12 @@ onMounted(()=>{
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 10px;
|
padding: 10px 12px;
|
||||||
min-height: 36px;
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 13px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -350,7 +350,7 @@ onMounted(()=>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-material {
|
.card-material {
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -361,17 +361,17 @@ onMounted(()=>{
|
|||||||
.card-progress {
|
.card-progress {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 12px 0 8px;
|
padding: 14px 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-text {
|
.progress-text {
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-stats {
|
.card-stats {
|
||||||
padding: 0 12px 8px;
|
padding: 0 12px 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
@ -380,7 +380,7 @@ onMounted(()=>{
|
|||||||
.stat-row {
|
.stat-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
:title="step.label"
|
:title="step.label"
|
||||||
>
|
>
|
||||||
<span class="flow-dot">
|
<span class="flow-dot">
|
||||||
<n-icon v-if="step.state === 'done'" size="12"><CheckmarkOutline /></n-icon>
|
<n-icon v-if="step.state === 'done'" size="14"><CheckmarkOutline /></n-icon>
|
||||||
<span v-else class="flow-num">{{ idx + 1 }}</span>
|
<span v-else class="flow-num">{{ idx + 1 }}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="flow-label">{{ step.shortLabel }}</span>
|
<span class="flow-label">{{ step.shortLabel }}</span>
|
||||||
@ -51,7 +51,7 @@ const steps = computed(() => {
|
|||||||
return {
|
return {
|
||||||
key: item.planId ?? `${item.processCode}-${idx}`,
|
key: item.planId ?? `${item.processCode}-${idx}`,
|
||||||
label,
|
label,
|
||||||
shortLabel: label.length > 5 ? `${label.slice(0, 5)}…` : label,
|
shortLabel: label.length > 6 ? `${label.slice(0, 6)}…` : label,
|
||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -73,9 +73,9 @@ function onClick() {
|
|||||||
|
|
||||||
.flow-stepper.clickable {
|
.flow-stepper.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 6px;
|
border-radius: 8px;
|
||||||
padding: 4px 6px;
|
padding: 6px 8px;
|
||||||
margin: -4px -6px;
|
margin: -6px -8px;
|
||||||
transition: background 0.15s;
|
transition: background 0.15s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,19 +87,19 @@ function onClick() {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
gap: 5px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
max-width: 56px;
|
max-width: 68px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-dot {
|
.flow-dot {
|
||||||
width: 22px;
|
width: 26px;
|
||||||
height: 22px;
|
height: 26px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border: 2px solid #d9d9d9;
|
border: 2px solid #d9d9d9;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -120,7 +120,7 @@ function onClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.flow-label {
|
.flow-label {
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
@ -134,12 +134,12 @@ function onClick() {
|
|||||||
|
|
||||||
.flow-line {
|
.flow-line {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 12px;
|
min-width: 14px;
|
||||||
max-width: 28px;
|
max-width: 32px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: #e8e8e8;
|
background: #e8e8e8;
|
||||||
margin: 0 2px;
|
margin: 0 3px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-line.done,
|
.flow-line.done,
|
||||||
|
|||||||
@ -0,0 +1,314 @@
|
|||||||
|
<template>
|
||||||
|
<n-modal
|
||||||
|
:show="show"
|
||||||
|
preset="card"
|
||||||
|
title="工单打印预览"
|
||||||
|
:mask-closable="false"
|
||||||
|
:style="{ width: 'min(980px, 96vw)' }"
|
||||||
|
@update:show="emit('update:show', $event)"
|
||||||
|
>
|
||||||
|
<n-spin :show="loading">
|
||||||
|
<div class="wo-preview-stage">
|
||||||
|
<div v-if="!order && !loading" class="wo-preview-empty">暂无工单数据</div>
|
||||||
|
<div v-else ref="sheetsRef" class="wo-preview-pages">
|
||||||
|
<section
|
||||||
|
v-for="page in pages"
|
||||||
|
:key="page.index"
|
||||||
|
class="wo-a4-sheet"
|
||||||
|
>
|
||||||
|
<div class="wo-a4-frame">
|
||||||
|
<header class="wo-a4-head" :class="{ 'is-cont': page.index > 1 }">
|
||||||
|
<div>
|
||||||
|
<h1 class="wo-a4-title">
|
||||||
|
{{ page.index === 1 ? '工序计划工单' : '工序计划工单(续)' }}
|
||||||
|
</h1>
|
||||||
|
<div class="wo-a4-id">工单号 <b>{{ workOrderCode || '未赋码' }}</b></div>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-qr">
|
||||||
|
<div class="wo-a4-qr-box">
|
||||||
|
<img v-if="qrDataUrl" :src="qrDataUrl" alt="工单号二维码" />
|
||||||
|
<div v-else class="wo-a4-qr-empty">无码</div>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-qr-cap">扫码</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<dl v-if="page.index === 1" class="wo-a4-meta">
|
||||||
|
<div class="wo-a4-meta-item">
|
||||||
|
<dt>生产令号</dt>
|
||||||
|
<dd>{{ text(order?.mainCode) }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-meta-item">
|
||||||
|
<dt>生产订单</dt>
|
||||||
|
<dd>{{ text(order?.orderCode) }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-meta-item">
|
||||||
|
<dt>计划数量</dt>
|
||||||
|
<dd>{{ order?.quantity ?? '-' }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-meta-item">
|
||||||
|
<dt>生产车间</dt>
|
||||||
|
<dd>{{ text(order?.workshopName) }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-meta-item is-wide">
|
||||||
|
<dt>物料</dt>
|
||||||
|
<dd>{{ materialText }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-meta-item">
|
||||||
|
<dt>计划开工</dt>
|
||||||
|
<dd>{{ formatTime(order?.beginTime) }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-meta-item">
|
||||||
|
<dt>计划完工</dt>
|
||||||
|
<dd>{{ formatTime(order?.endTime) }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<div class="wo-a4-table-wrap">
|
||||||
|
<table class="wo-a4-table">
|
||||||
|
<colgroup>
|
||||||
|
<col class="col-no" />
|
||||||
|
<col class="col-name" />
|
||||||
|
<col class="col-time" />
|
||||||
|
<col class="col-time" />
|
||||||
|
<col class="col-qty" />
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>工序号</th>
|
||||||
|
<th>工序名称</th>
|
||||||
|
<th>计划开始</th>
|
||||||
|
<th>计划结束</th>
|
||||||
|
<th>数量</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="(row, idx) in page.rows"
|
||||||
|
:key="row.planId ?? `${page.index}-${idx}`"
|
||||||
|
>
|
||||||
|
<td>{{ row.operNumber ?? '-' }}</td>
|
||||||
|
<td class="is-left">{{ text(row.processName) }}</td>
|
||||||
|
<td>{{ formatTime(row.planStartTime || row.beginTime) }}</td>
|
||||||
|
<td>{{ formatTime(row.planFinishTime || row.endTime) }}</td>
|
||||||
|
<td>{{ row.quantity ?? '-' }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div v-if="!page.rows.length" class="wo-a4-empty">本工单暂无工序计划</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer v-if="page.index === pages.length" class="wo-a4-foot">
|
||||||
|
<div class="wo-a4-sign">
|
||||||
|
<div class="wo-a4-sign-cell">
|
||||||
|
<b>操作工</b>
|
||||||
|
<div class="wo-a4-sign-line" />
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-sign-cell">
|
||||||
|
<b>质检</b>
|
||||||
|
<div class="wo-a4-sign-line" />
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-sign-cell">
|
||||||
|
<b>班组确认</b>
|
||||||
|
<div class="wo-a4-sign-line" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wo-a4-note">
|
||||||
|
<span>打印人 {{ printerName }} {{ printedAt }}</span>
|
||||||
|
<span>{{ page.index }} / {{ page.total }}</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<div v-else class="wo-a4-note">
|
||||||
|
<span>续页 {{ workOrderCode || '-' }}</span>
|
||||||
|
<span>{{ page.index }} / {{ page.total }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-spin>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<n-space justify="end">
|
||||||
|
<n-button @click="emit('update:show', false)">关闭</n-button>
|
||||||
|
<n-button type="primary" :disabled="!order || loading" @click="handlePrint">
|
||||||
|
打印
|
||||||
|
</n-button>
|
||||||
|
</n-space>
|
||||||
|
</template>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import { NButton, NModal, NSpace, NSpin, useMessage } from 'naive-ui'
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
import printCss from './workOrderPrint.css?inline'
|
||||||
|
import './workOrderPrint.css'
|
||||||
|
import {
|
||||||
|
type OrderProcessPlanVO,
|
||||||
|
type ProcessPlanItemVO,
|
||||||
|
} from '@/api/orderProcessPlan'
|
||||||
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
|
const FIRST_PAGE_ROWS = 16
|
||||||
|
const NEXT_PAGE_ROWS = 20
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
show: boolean
|
||||||
|
order: OrderProcessPlanVO | null
|
||||||
|
loading?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:show': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const sheetsRef = ref<HTMLElement | null>(null)
|
||||||
|
const qrDataUrl = ref('')
|
||||||
|
const printedAt = ref('')
|
||||||
|
|
||||||
|
const workOrderCode = computed(() => props.order?.workOrderCode?.trim() || '')
|
||||||
|
const printerName = computed(() => userStore.nickname || '-')
|
||||||
|
|
||||||
|
const materialText = computed(() => {
|
||||||
|
const name = props.order?.materialName || '-'
|
||||||
|
const code = props.order?.materialCode
|
||||||
|
return code ? `${name} ${code}` : name
|
||||||
|
})
|
||||||
|
|
||||||
|
const sortedProcesses = computed(() => {
|
||||||
|
return [...(props.order?.processList ?? [])].sort((a, b) => {
|
||||||
|
const oa = a.operNumber ?? 0
|
||||||
|
const ob = b.operNumber ?? 0
|
||||||
|
if (oa !== ob) return oa - ob
|
||||||
|
return (a.planId ?? 0) - (b.planId ?? 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const pages = computed(() => {
|
||||||
|
const list = sortedProcesses.value
|
||||||
|
if (!list.length) {
|
||||||
|
return [{ index: 1, total: 1, rows: [] as ProcessPlanItemVO[] }]
|
||||||
|
}
|
||||||
|
const chunks: ProcessPlanItemVO[][] = [list.slice(0, FIRST_PAGE_ROWS)]
|
||||||
|
for (let i = FIRST_PAGE_ROWS; i < list.length; i += NEXT_PAGE_ROWS) {
|
||||||
|
chunks.push(list.slice(i, i + NEXT_PAGE_ROWS))
|
||||||
|
}
|
||||||
|
return chunks.map((rows, i) => ({
|
||||||
|
index: i + 1,
|
||||||
|
total: chunks.length,
|
||||||
|
rows,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
function text(v?: string | null) {
|
||||||
|
return v && String(v).trim() ? String(v) : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(val?: string | null) {
|
||||||
|
if (!val) return '-'
|
||||||
|
return val.length > 16 ? val.slice(0, 16) : val
|
||||||
|
}
|
||||||
|
|
||||||
|
function nowText() {
|
||||||
|
const d = new Date()
|
||||||
|
const p = (n: number) => String(n).padStart(2, '0')
|
||||||
|
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.show, workOrderCode.value] as const,
|
||||||
|
async ([visible, code]) => {
|
||||||
|
if (!visible) return
|
||||||
|
printedAt.value = nowText()
|
||||||
|
if (!code) {
|
||||||
|
qrDataUrl.value = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
qrDataUrl.value = await QRCode.toDataURL(code, {
|
||||||
|
errorCorrectionLevel: 'M',
|
||||||
|
margin: 1,
|
||||||
|
width: 320,
|
||||||
|
color: { dark: '#000000', light: '#ffffff' },
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
qrDataUrl.value = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
function handlePrint() {
|
||||||
|
const html = sheetsRef.value?.innerHTML
|
||||||
|
if (!html) {
|
||||||
|
message.warning('没有可打印的内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const iframe = document.createElement('iframe')
|
||||||
|
iframe.setAttribute('aria-hidden', 'true')
|
||||||
|
iframe.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;'
|
||||||
|
document.body.appendChild(iframe)
|
||||||
|
const doc = iframe.contentDocument
|
||||||
|
if (!doc) {
|
||||||
|
iframe.remove()
|
||||||
|
message.error('无法打开打印预览')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const title = workOrderCode.value || '工序计划工单'
|
||||||
|
doc.open()
|
||||||
|
doc.write(`<!DOCTYPE html><html><head><meta charset="utf-8"><title>${title}</title><style>${printCss}html,body{margin:0;background:#fff}</style></head><body>${html}</body></html>`)
|
||||||
|
doc.close()
|
||||||
|
const run = () => {
|
||||||
|
iframe.contentWindow?.focus()
|
||||||
|
iframe.contentWindow?.print()
|
||||||
|
window.setTimeout(() => iframe.remove(), 800)
|
||||||
|
}
|
||||||
|
const imgs = Array.from(doc.images)
|
||||||
|
if (!imgs.length) {
|
||||||
|
run()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Promise.all(
|
||||||
|
imgs.map(
|
||||||
|
(img) =>
|
||||||
|
new Promise<void>((resolve) => {
|
||||||
|
if (img.complete) resolve()
|
||||||
|
else {
|
||||||
|
img.onload = () => resolve()
|
||||||
|
img.onerror = () => resolve()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).then(run)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.wo-preview-stage {
|
||||||
|
max-height: 72vh;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 12px;
|
||||||
|
background: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-preview-empty {
|
||||||
|
padding: 48px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-preview-pages {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-preview-pages :deep(.wo-a4-sheet) {
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
236
src/views/biz/orderProcessPlan/components/workOrderPrint.css
Normal file
236
src/views/biz/orderProcessPlan/components/workOrderPrint.css
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/* A4 工单:黑白、简洁,供预览与打印共用 */
|
||||||
|
|
||||||
|
.wo-a4-sheet {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 210mm;
|
||||||
|
height: 297mm;
|
||||||
|
padding: 12mm 14mm 12mm;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
page-break-after: always;
|
||||||
|
break-after: page;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-sheet:last-child {
|
||||||
|
page-break-after: auto;
|
||||||
|
break-after: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-frame {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 5mm 5mm 4mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-head {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 32mm;
|
||||||
|
gap: 6mm;
|
||||||
|
align-items: start;
|
||||||
|
padding-bottom: 4mm;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-head.is-cont {
|
||||||
|
grid-template-columns: 1fr 18mm;
|
||||||
|
padding-bottom: 3mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.2em;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-id {
|
||||||
|
margin-top: 3mm;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-id b {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-qr {
|
||||||
|
width: 32mm;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-head.is-cont .wo-a4-qr {
|
||||||
|
width: 18mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-qr-box {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 32mm;
|
||||||
|
height: 32mm;
|
||||||
|
padding: 1mm;
|
||||||
|
border: 1px solid #000;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-head.is-cont .wo-a4-qr-box {
|
||||||
|
width: 18mm;
|
||||||
|
height: 18mm;
|
||||||
|
padding: 0.5mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-qr-box img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-qr-empty {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-qr-cap {
|
||||||
|
margin-top: 1mm;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
|
border: 1px solid #000;
|
||||||
|
border-top: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta-item {
|
||||||
|
padding: 2.2mm 3mm;
|
||||||
|
border-right: 1px solid #000;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta-item:nth-child(4n),
|
||||||
|
.wo-a4-meta-item:last-child {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta-item:nth-child(n + 5) {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta-item.is-wide {
|
||||||
|
grid-column: span 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta-item dt {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-meta-item dd {
|
||||||
|
margin: 1mm 0 0;
|
||||||
|
font-weight: 600;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-table-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
margin-top: 4mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-table th,
|
||||||
|
.wo-a4-table td {
|
||||||
|
border: 1px solid #000;
|
||||||
|
padding: 1.8mm 1.6mm;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
word-break: break-all;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-table th {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-table td.is-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-table .col-no { width: 16mm; }
|
||||||
|
.wo-a4-table .col-name { width: auto; }
|
||||||
|
.wo-a4-table .col-time { width: 36mm; }
|
||||||
|
.wo-a4-table .col-qty { width: 18mm; }
|
||||||
|
|
||||||
|
.wo-a4-empty {
|
||||||
|
padding: 10mm 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-foot {
|
||||||
|
margin-top: 5mm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-sign {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-sign-cell {
|
||||||
|
min-height: 16mm;
|
||||||
|
padding: 2mm 3mm 2.5mm;
|
||||||
|
border-right: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-sign-cell:last-child {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-sign-cell b {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-sign-line {
|
||||||
|
margin-top: 8mm;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wo-a4-note {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 3mm;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
@page {
|
||||||
|
size: A4 portrait;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,7 +7,13 @@
|
|||||||
<template #icon><n-icon><AddOutline /></n-icon></template>
|
<template #icon><n-icon><AddOutline /></n-icon></template>
|
||||||
新建工序委外
|
新建工序委外
|
||||||
</n-button>
|
</n-button>
|
||||||
<n-button disabled title="待后端接口支持">下推</n-button>
|
<n-button
|
||||||
|
type="primary"
|
||||||
|
ghost
|
||||||
|
:disabled="selectedIds.length !== 1"
|
||||||
|
title="需先填写供应商,已下推不可再推"
|
||||||
|
@click="handlePushKingdee()"
|
||||||
|
>下推金蝶</n-button>
|
||||||
<n-button type="error" :disabled="selectedIds.length === 0" @click="handleBatchDelete">
|
<n-button type="error" :disabled="selectedIds.length === 0" @click="handleBatchDelete">
|
||||||
<template #icon><n-icon><TrashOutline /></n-icon></template>
|
<template #icon><n-icon><TrashOutline /></n-icon></template>
|
||||||
批量删除
|
批量删除
|
||||||
@ -58,7 +64,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:row-key="(row) => row.id!"
|
:row-key="(row) => row.id!"
|
||||||
:scroll-x="1520"
|
:scroll-x="1720"
|
||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
@update:page-size="handlePageSizeChange"
|
@update:page-size="handlePageSizeChange"
|
||||||
@update:checked-row-keys="handleCheck"
|
@update:checked-row-keys="handleCheck"
|
||||||
@ -82,8 +88,21 @@
|
|||||||
<n-descriptions-item label="合格数量">{{ formatDecimal(detailData.qualifiedQuantity) }}</n-descriptions-item>
|
<n-descriptions-item label="合格数量">{{ formatDecimal(detailData.qualifiedQuantity) }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="预计开始">{{ detailData.beginPreparationTime || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="预计开始">{{ detailData.beginPreparationTime || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="预计结束">{{ detailData.endPreparationTime || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="预计结束">{{ detailData.endPreparationTime || '-' }}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="下推金蝶">{{ detailData.kingdeePushed === 1 ? '已下推' : '未下推' }}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="金蝶单号">{{ detailData.kingdeeBillNo || '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="备注" :span="2">{{ detailData.remark || '-' }}</n-descriptions-item>
|
<n-descriptions-item label="备注" :span="2">{{ detailData.remark || '-' }}</n-descriptions-item>
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
|
<div v-if="detailData.details?.length" style="margin-top: 16px">
|
||||||
|
<div style="margin-bottom: 8px; font-weight: 600">分批明细</div>
|
||||||
|
<n-data-table
|
||||||
|
size="small"
|
||||||
|
:bordered="true"
|
||||||
|
:single-line="false"
|
||||||
|
:data="detailData.details"
|
||||||
|
:columns="detailColumns"
|
||||||
|
:pagination="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
<!-- 编辑 -->
|
<!-- 编辑 -->
|
||||||
@ -97,10 +116,16 @@
|
|||||||
<n-input :value="formData.name" disabled />
|
<n-input :value="formData.name" disabled />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi label="供应商" path="supplierName">
|
<n-form-item-gi label="供应商" path="supplierName">
|
||||||
<n-input v-model:value="formData.supplierName" placeholder="请输入供应商" />
|
<n-input v-model:value="formData.supplierName" placeholder="空则为草稿,补齐后可下推金蝶" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi label="委外数量" path="quantity">
|
<n-form-item-gi label="委外数量" path="quantity">
|
||||||
<n-input-number v-model:value="formData.quantity" :min="1" :precision="0" style="width: 100%" />
|
<n-input-number
|
||||||
|
v-model:value="formData.quantity"
|
||||||
|
:min="1"
|
||||||
|
:precision="0"
|
||||||
|
:disabled="formData.kingdeePushed === 1 || !!formData.details?.length"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi label="单价" path="unitPrice">
|
<n-form-item-gi label="单价" path="unitPrice">
|
||||||
<n-input-number v-model:value="formData.unitPrice" :min="0" :precision="2" style="width: 100%" />
|
<n-input-number v-model:value="formData.unitPrice" :min="0" :precision="2" style="width: 100%" />
|
||||||
@ -202,11 +227,12 @@ const defaultFormData = (): Record<string, unknown> => ({
|
|||||||
beginPreparationTime: null,
|
beginPreparationTime: null,
|
||||||
endPreparationTime: null,
|
endPreparationTime: null,
|
||||||
remark: '',
|
remark: '',
|
||||||
|
kingdeePushed: 0,
|
||||||
|
details: [],
|
||||||
})
|
})
|
||||||
const formData = reactive<Record<string, unknown>>(defaultFormData())
|
const formData = reactive<Record<string, unknown>>(defaultFormData())
|
||||||
|
|
||||||
const formRules: FormRules = {
|
const formRules: FormRules = {
|
||||||
supplierName: [{ required: true, message: '请输入供应商', trigger: 'blur' }],
|
|
||||||
quantity: [{ required: true, type: 'number', message: '请输入委外数量', trigger: ['blur', 'change'] }],
|
quantity: [{ required: true, type: 'number', message: '请输入委外数量', trigger: ['blur', 'change'] }],
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,18 +384,40 @@ const columns: DataTableColumns<Outsourcing> = [
|
|||||||
])
|
])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '下推金蝶',
|
||||||
|
key: 'kingdeePushed',
|
||||||
|
width: 100,
|
||||||
|
align: 'center',
|
||||||
|
render(row) {
|
||||||
|
const pushed = row.kingdeePushed === 1
|
||||||
|
return h(NTag, {
|
||||||
|
size: 'small',
|
||||||
|
type: pushed ? 'success' : 'default',
|
||||||
|
bordered: false,
|
||||||
|
}, { default: () => pushed ? '已下推' : '未下推' })
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width: 120,
|
width: 168,
|
||||||
align: 'center',
|
align: 'center',
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
render(row) {
|
render(row) {
|
||||||
|
const canPush = row.kingdeePushed !== 1 && !!row.supplierName
|
||||||
return h('div', { class: 'cell-center' }, [
|
return h('div', { class: 'cell-center' }, [
|
||||||
h(NSpace, { size: 4, justify: 'center' }, {
|
h(NSpace, { size: 4, justify: 'center' }, {
|
||||||
default: () => [
|
default: () => [
|
||||||
h(NButton, { text: true, type: 'primary', size: 'small', onClick: () => openDetail(row) }, { default: () => '详情' }),
|
h(NButton, { text: true, type: 'primary', size: 'small', onClick: () => openDetail(row) }, { default: () => '详情' }),
|
||||||
h(NButton, { text: true, type: 'primary', size: 'small', onClick: () => handleEdit(row) }, { default: () => '编辑' }),
|
h(NButton, { text: true, type: 'primary', size: 'small', onClick: () => handleEdit(row) }, { default: () => '编辑' }),
|
||||||
|
h(NButton, {
|
||||||
|
text: true,
|
||||||
|
type: 'primary',
|
||||||
|
size: 'small',
|
||||||
|
disabled: !canPush,
|
||||||
|
onClick: () => handlePushKingdee(row.id),
|
||||||
|
}, { default: () => '下推' }),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
@ -377,6 +425,13 @@ const columns: DataTableColumns<Outsourcing> = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const detailColumns: DataTableColumns<{ quantity?: number; remark?: string; createTime?: string; flowingAroundId?: number }> = [
|
||||||
|
{ title: '批次数量', key: 'quantity', width: 90, align: 'center' },
|
||||||
|
{ title: '来源', key: 'remark', ellipsis: { tooltip: true } },
|
||||||
|
{ title: '流转确认ID', key: 'flowingAroundId', width: 110, align: 'center', render: (row) => row.flowingAroundId ?? '-' },
|
||||||
|
{ title: '时间', key: 'createTime', width: 160, align: 'center', render: (row) => row.createTime || '-' },
|
||||||
|
]
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@ -440,13 +495,15 @@ async function openDetail(row: Outsourcing) {
|
|||||||
detailVisible.value = true
|
detailVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEdit(row: Outsourcing) {
|
async function handleEdit(row: Outsourcing) {
|
||||||
|
if (!row.id) return
|
||||||
modalTitle.value = '编辑委外工单'
|
modalTitle.value = '编辑委外工单'
|
||||||
Object.assign(formData, defaultFormData(), row, {
|
const full = await outsourcingApi.detail(row.id)
|
||||||
quantity: row.quantity ?? null,
|
Object.assign(formData, defaultFormData(), full, {
|
||||||
unitPrice: row.unitPrice != null ? Number(row.unitPrice) : null,
|
quantity: full.quantity ?? null,
|
||||||
beginPreparationTime: parseDateTime(row.beginPreparationTime),
|
unitPrice: full.unitPrice != null ? Number(full.unitPrice) : null,
|
||||||
endPreparationTime: parseDateTime(row.endPreparationTime),
|
beginPreparationTime: parseDateTime(full.beginPreparationTime),
|
||||||
|
endPreparationTime: parseDateTime(full.endPreparationTime),
|
||||||
})
|
})
|
||||||
modalVisible.value = true
|
modalVisible.value = true
|
||||||
}
|
}
|
||||||
@ -483,6 +540,34 @@ async function handleSubmit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePushKingdee(id?: number) {
|
||||||
|
const targetId = id ?? selectedIds.value[0]
|
||||||
|
if (!targetId) {
|
||||||
|
message.warning('请选择一张委外单')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const row = tableData.value.find((r) => r.id === targetId)
|
||||||
|
if (row?.kingdeePushed === 1) {
|
||||||
|
message.warning('该委外单已下推金蝶')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!row?.supplierName) {
|
||||||
|
message.warning('请先填写供应商,再下推金蝶')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialog.warning({
|
||||||
|
title: '下推金蝶',
|
||||||
|
content: `确定下推委外单 ${row.outsourceCode || targetId} 吗?下推后不能再改数量。`,
|
||||||
|
positiveText: '确定',
|
||||||
|
negativeText: '取消',
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
await outsourcingApi.pushKingdee(targetId)
|
||||||
|
message.success('已标记下推金蝶')
|
||||||
|
loadData()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function handleBatchDelete() {
|
function handleBatchDelete() {
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
|
|||||||
@ -1,19 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<n-scrollbar x-scrollable>
|
<n-data-table
|
||||||
<n-data-table
|
|
||||||
v-model:expanded-row-keys="AssingWorkExpandedKeys"
|
v-model:expanded-row-keys="AssingWorkExpandedKeys"
|
||||||
:columns="AssingWorkColumns"
|
:columns="AssingWorkColumns"
|
||||||
:data="AssingWorkTableData"
|
:data="AssingWorkTableData"
|
||||||
:loading="tableLoading"
|
:loading="tableLoading"
|
||||||
:row-key="RowKey"
|
:row-key="RowKey"
|
||||||
:scroll-x="1700"
|
:scroll-x="1920"
|
||||||
:max-height="1000"
|
:max-height="1000"
|
||||||
size="small"
|
size="small"
|
||||||
|
striped
|
||||||
|
:single-line="true"
|
||||||
|
class="assing-work-table"
|
||||||
style="margin-bottom:15px"
|
style="margin-bottom:15px"
|
||||||
@update-expanded-row-keys="handleExpandChange"
|
@update-expanded-row-keys="handleExpandChange"
|
||||||
/>
|
/>
|
||||||
</n-scrollbar>
|
|
||||||
|
|
||||||
<div class="pagination-container">
|
<div class="pagination-container">
|
||||||
<n-button
|
<n-button
|
||||||
@ -40,8 +41,14 @@
|
|||||||
</n-pagination>
|
</n-pagination>
|
||||||
</div>
|
</div>
|
||||||
<!--完工汇报弹窗-->
|
<!--完工汇报弹窗-->
|
||||||
<n-modal v-model:show="showQcReport" :title="reportTitle" style="width: 1000px" >
|
<n-modal
|
||||||
<div>
|
v-model:show="showQcReport"
|
||||||
|
preset="card"
|
||||||
|
:title="reportTitle"
|
||||||
|
:bordered="false"
|
||||||
|
style="width: 920px"
|
||||||
|
:segmented="{ content: true, footer: true }"
|
||||||
|
>
|
||||||
<SubmitLogInfoPage
|
<SubmitLogInfoPage
|
||||||
v-if="baseInfo.logOrQuality === 'log'"
|
v-if="baseInfo.logOrQuality === 'log'"
|
||||||
:assingId="assingId"
|
:assingId="assingId"
|
||||||
@ -52,13 +59,9 @@
|
|||||||
:assingId="assingId"
|
:assingId="assingId"
|
||||||
:baseInfo="baseInfo"
|
:baseInfo="baseInfo"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<n-space justify="end">
|
<n-space justify="end">
|
||||||
<n-button @click="showQcReport = false">取消</n-button>
|
<n-button @click="showQcReport = false">关闭</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
@ -78,6 +81,10 @@
|
|||||||
<n-descriptions-item label="是否返工">{{ handlerReWork(assingWorkDetail.rework) ?? '-' }}</n-descriptions-item>
|
<n-descriptions-item label="是否返工">{{ handlerReWork(assingWorkDetail.rework) ?? '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="返工数量">{{ assingWorkDetail.reworkNum ?? '-' }}</n-descriptions-item>
|
<n-descriptions-item label="返工数量">{{ assingWorkDetail.reworkNum ?? '-' }}</n-descriptions-item>
|
||||||
<n-descriptions-item label="派工状态">{{ handlerPatchStatus(assingWorkDetail.assingStatus) ?? '-' }}</n-descriptions-item>
|
<n-descriptions-item label="派工状态">{{ handlerPatchStatus(assingWorkDetail.assingStatus) ?? '-' }}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="暂停时间">{{ formatWorkSeconds(assingWorkDetail.pauseSeconds) }}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="调车时间">{{ formatWorkSeconds(assingWorkDetail.shuntSeconds) }}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="实际工时">{{ formatWorkSeconds(assingWorkDetail.actualWorkSeconds) }}</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="单件工时">{{ formatWorkSeconds(assingWorkDetail.unitWorkSeconds) }}</n-descriptions-item>
|
||||||
|
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
|
|
||||||
@ -104,13 +111,56 @@ import {
|
|||||||
|
|
||||||
import { mytasks,type AssingWork } from '@/api/production';
|
import { mytasks,type AssingWork } from '@/api/production';
|
||||||
import {
|
import {
|
||||||
NButton, NSpace, NTag, NDataTable, DataTableColumns, NDescriptions, NDescriptionsItem, NPagination
|
NButton, NSpace, NTag, NDataTable, NIcon, DataTableColumns, NDescriptions, NDescriptionsItem, NPagination
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
|
import { DocumentTextOutline, ShieldCheckmarkOutline } from '@vicons/ionicons5'
|
||||||
import { dictDataApi } from '@/api/org'
|
import { dictDataApi } from '@/api/org'
|
||||||
import { assingWorkDetailApi,AssingWorkDetail } from '@/api/AssingWorkDetail'
|
import { assingWorkDetailApi,AssingWorkDetail } from '@/api/AssingWorkDetail'
|
||||||
import SubmitLogInfoPage from "@/views/production/componets/SubmitLogInfoPage.vue";
|
import SubmitLogInfoPage from "@/views/production/componets/SubmitLogInfoPage.vue";
|
||||||
import QualityInfoPage from "@/views/production/componets/QualityInfoPage.vue";
|
import QualityInfoPage from "@/views/production/componets/QualityInfoPage.vue";
|
||||||
import Button from "naive-ui/es/button/src/Button";
|
import { formatWorkSeconds, getaDtetime } from '@/utils/tool'
|
||||||
|
|
||||||
|
function dash(value: unknown) {
|
||||||
|
if (value === 0) return '0'
|
||||||
|
if (value == null || value === '') return '-'
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderInline(primary?: unknown, secondary?: unknown) {
|
||||||
|
const main = dash(primary)
|
||||||
|
const sub = dash(secondary)
|
||||||
|
const title = sub === '-' ? main : `${main} ${sub}`
|
||||||
|
return h('div', { class: 'cell-inline', title }, [
|
||||||
|
h('span', { class: 'cell-main' }, main),
|
||||||
|
sub !== '-' ? h('span', { class: 'cell-sub' }, sub) : null
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFlag(label: string, value: unknown) {
|
||||||
|
const yes = value == 1
|
||||||
|
const no = value == 0
|
||||||
|
return h('span', {
|
||||||
|
class: ['flag-chip', yes ? 'is-yes' : no ? 'is-no' : 'is-empty']
|
||||||
|
}, `${label}${yes ? '是' : no ? '否' : '-'}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactHours(seconds?: number | string | null) {
|
||||||
|
return formatWorkSeconds(seconds).replace('小时', 'h').replace('分', 'm').replace('秒', 's')
|
||||||
|
}
|
||||||
|
|
||||||
|
function completedQtyOf(row: any) {
|
||||||
|
return Number(row.completedQuantity ?? row.completeQuantity) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function planQtyOf(row: any) {
|
||||||
|
return Number(row.quantity) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCompletedTime(value: unknown) {
|
||||||
|
if (!value) return '-'
|
||||||
|
if (typeof value === 'number' || value instanceof Date) return getaDtetime(value)
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const defaultBaseInfo = ref({
|
const defaultBaseInfo = ref({
|
||||||
@ -161,12 +211,7 @@ const AssingWorkColumns :DataTableColumns<AssingWork> = [
|
|||||||
{
|
{
|
||||||
class: 'route-code-link',
|
class: 'route-code-link',
|
||||||
href: 'javascript:;',
|
href: 'javascript:;',
|
||||||
style: {
|
onClick: () => {
|
||||||
color: '#2080f0',
|
|
||||||
textDecoration: 'underline',
|
|
||||||
cursor: 'pointer',
|
|
||||||
},
|
|
||||||
onClick: (e: MouseEvent) => {
|
|
||||||
viewAssingWorkDetail(row)
|
viewAssingWorkDetail(row)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -175,203 +220,144 @@ const AssingWorkColumns :DataTableColumns<AssingWork> = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
title: '工单 / 工序',
|
||||||
|
key: 'workOrderCode',
|
||||||
|
width: 240,
|
||||||
|
render(row: any) {
|
||||||
|
return renderInline(row.workOrderCode, row.processName)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
title: '物料编码',
|
title: '物料编码',
|
||||||
key: 'materialCode',
|
key: 'materialCode',
|
||||||
width: 120,
|
width: 120,
|
||||||
ellipsis: { tooltip: true },
|
ellipsis: false,
|
||||||
render(row:any) {
|
className: 'material-full-cell',
|
||||||
return row.materialCode || '-'
|
render(row: any) {
|
||||||
|
return h('span', { class: 'cell-full' }, dash(row.materialCode))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
|
||||||
title: '物料名称',
|
title: '物料名称',
|
||||||
key: 'materialName',
|
key: 'materialName',
|
||||||
width: 140,
|
width: 120,
|
||||||
|
ellipsis: false,
|
||||||
|
className: 'material-full-cell',
|
||||||
|
render(row: any) {
|
||||||
|
return h('span', { class: 'cell-full' }, dash(row.materialName))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '工位 / 设备',
|
||||||
|
key: 'deviceName',
|
||||||
|
width: 120,
|
||||||
ellipsis: { tooltip: true },
|
ellipsis: { tooltip: true },
|
||||||
render(row:any) {
|
render(row: any) {
|
||||||
return row.materialName || '-'
|
return dash(row.deviceName)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
align: 'center',
|
||||||
title: '工单号',
|
title: '质检 / 入库',
|
||||||
key: 'workOrderCode',
|
|
||||||
width: 120,
|
|
||||||
ellipsis: { tooltip: true }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align:'center',
|
|
||||||
title: '工序名称',
|
|
||||||
key: 'processName',
|
|
||||||
width: 120,
|
|
||||||
ellipsis: { tooltip: true }
|
|
||||||
},
|
|
||||||
/*{
|
|
||||||
align:'center',
|
|
||||||
title: '工序编号',
|
|
||||||
key: 'processCode',
|
|
||||||
width: 100
|
|
||||||
}, */
|
|
||||||
{
|
|
||||||
align:'center',
|
|
||||||
title: '是否质检',
|
|
||||||
key: 'qualityInspection',
|
key: 'qualityInspection',
|
||||||
width: 90,
|
width: 126,
|
||||||
render(row) {
|
render(row: any) {
|
||||||
var opt = row.qualityInspection
|
return h('div', { class: 'flag-group' }, [
|
||||||
if (opt == 0) {
|
renderFlag('质检', row.qualityInspection),
|
||||||
return h(NTag, { type: 'error', size: 'small' }, { default: () => "否" })
|
renderFlag('入库', row.storageEntry)
|
||||||
}
|
])
|
||||||
if (opt == 1) {
|
|
||||||
return h(NTag, { type: 'success', size: 'small' }, { default: () => "是" })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
align: 'center',
|
||||||
title: '是否入库',
|
|
||||||
key: 'storageEntry',
|
|
||||||
width: 90,
|
|
||||||
render(row) {
|
|
||||||
var opt = row.storageEntry
|
|
||||||
if (opt == 0) {
|
|
||||||
return h(NTag, { type: 'error', size: 'small' }, { default: () => "否" })
|
|
||||||
}
|
|
||||||
if (opt == 1) {
|
|
||||||
return h(NTag, { type: 'success', size: 'small' }, { default: () => "是" })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
align:'center',
|
|
||||||
title: '派工状态',
|
title: '派工状态',
|
||||||
key: 'assingStatus',
|
key: 'assingStatus',
|
||||||
width: 90,
|
width: 96,
|
||||||
render(row:any) {
|
render(row: any) {
|
||||||
const val = row.assingStatus
|
const val = row.assingStatus
|
||||||
const opt = qcAssingStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
const opt = qcAssingStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
||||||
if (!opt) return val ?? '-'
|
if (!opt) return dash(val)
|
||||||
|
return h(NTag, { type: opt.class, size: 'small', bordered: false }, { default: () => opt.label })
|
||||||
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
|
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// align:'center',
|
|
||||||
// title: '车间名称',
|
|
||||||
// key: 'workshopName',
|
|
||||||
// minWidth: 200
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// align:'center',
|
|
||||||
// title: '工段名称',
|
|
||||||
// key: 'sectionName',
|
|
||||||
// minWidth: 200
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
align:'center',
|
title: '完成进度',
|
||||||
title: '工位/设备',
|
key: 'quantity',
|
||||||
key: 'deviceName',
|
width: 88,
|
||||||
width: 110,
|
render(row: any) {
|
||||||
ellipsis: { tooltip: true }
|
return h('span', { class: 'qty-text' }, `${completedQtyOf(row)}/${planQtyOf(row)}`)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
|
||||||
title: '数量',
|
|
||||||
width: 70,
|
|
||||||
key: 'quantity'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align:'center',
|
|
||||||
title: '完成数量',
|
|
||||||
key: 'completedQuantity',
|
|
||||||
width: 80
|
|
||||||
},
|
|
||||||
{
|
|
||||||
align:'center',
|
|
||||||
title: '完成时间',
|
title: '完成时间',
|
||||||
key: 'completedTime',
|
key: 'completedTime',
|
||||||
width: 160
|
width: 156,
|
||||||
|
render(row: any) {
|
||||||
|
return h('span', { class: 'time-text' }, formatCompletedTime(row.completedTime))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
title: '工时',
|
||||||
|
key: 'actualWorkSeconds',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: { tooltip: true },
|
||||||
|
render(row: any) {
|
||||||
|
const title = `暂停 ${formatWorkSeconds(row.pauseSeconds)} 调车 ${formatWorkSeconds(row.shuntSeconds)} 实际 ${formatWorkSeconds(row.actualWorkSeconds)} 单件 ${formatWorkSeconds(row.unitWorkSeconds)}`
|
||||||
|
return h('div', { class: 'hours-cell', title }, [
|
||||||
|
h('span', { class: 'hours-item' }, ['停 ', h('b', compactHours(row.pauseSeconds))]),
|
||||||
|
h('span', { class: 'hours-item' }, ['调 ', h('b', compactHours(row.shuntSeconds))]),
|
||||||
|
h('span', { class: 'hours-item is-actual' }, ['实 ', h('b', compactHours(row.actualWorkSeconds))]),
|
||||||
|
h('span', { class: 'hours-item is-unit' }, ['单 ', h('b', compactHours(row.unitWorkSeconds))])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align: 'center',
|
||||||
title: '状态',
|
title: '状态',
|
||||||
key: 'status',
|
key: 'status',
|
||||||
width: 80,
|
width: 84,
|
||||||
render(row:any) {
|
render(row: any) {
|
||||||
if(row.status == 0){
|
if (row.status == 0) {
|
||||||
return h(NTag, {
|
return h(NTag, { type: 'success', size: 'small', bordered: false }, { default: () => '启用' })
|
||||||
type:'success',
|
|
||||||
size: 'small'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '启用'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if(row.status == 1){
|
if (row.status == 1) {
|
||||||
return h(NTag, {
|
return h(NTag, { type: 'warning', size: 'small', bordered: false }, { default: () => '已暂停' })
|
||||||
type:'warning',
|
|
||||||
size: 'small'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '已暂停'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if(row.status == 2){
|
if (row.status == 2) {
|
||||||
return h(NTag, {
|
return h(NTag, { type: 'info', size: 'small', bordered: false }, { default: () => '已改派' })
|
||||||
type:'warning',
|
|
||||||
size: 'small'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => '已改派'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align:'center',
|
align: 'center',
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 180,
|
width: 196,
|
||||||
fixed: 'right',
|
fixed: 'right',
|
||||||
render(row:any) {
|
render(row: any) {
|
||||||
const buttons:any = []
|
return h('div', { class: 'action-cell' }, [
|
||||||
buttons.push(
|
h(NButton, {
|
||||||
h(
|
size: 'small',
|
||||||
NButton,
|
quaternary: true,
|
||||||
{
|
onClick: () => handleReportRecords(row)
|
||||||
size: 'small',
|
}, {
|
||||||
type: 'primary',
|
default: () => [
|
||||||
ghost:true,
|
h(NIcon, { size: 16 }, { default: () => h(DocumentTextOutline) }),
|
||||||
onClick: () => {
|
' 汇报信息'
|
||||||
handleReportRecords(row)
|
]
|
||||||
}
|
}),
|
||||||
},
|
h(NButton, {
|
||||||
{ default: () => '汇报信息' }
|
size: 'small',
|
||||||
)
|
quaternary: true,
|
||||||
)
|
onClick: () => handleQualityRecords(row)
|
||||||
buttons.push(
|
}, {
|
||||||
h(
|
default: () => [
|
||||||
NButton,
|
h(NIcon, { size: 16 }, { default: () => h(ShieldCheckmarkOutline) }),
|
||||||
{
|
' 质检信息'
|
||||||
size: 'small',
|
]
|
||||||
type: 'primary',
|
})
|
||||||
ghost:true,
|
])
|
||||||
onClick: () => {
|
|
||||||
handleQualityRecords(row)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ default: () => '质检信息' }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return buttons.length > 0 ? h(NSpace, {justify:'center'}, { default: () => buttons }) : '-'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,6 +386,33 @@ const AssingWorkDetailColums:DataTableColumns<AssingWorkDetail> = [
|
|||||||
key:"num",
|
key:"num",
|
||||||
width:80
|
width:80
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
align:"center",
|
||||||
|
title:"暂停时间",
|
||||||
|
key:"pauseSeconds",
|
||||||
|
width:120,
|
||||||
|
render(row: any) {
|
||||||
|
return formatWorkSeconds(row.pauseSeconds)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align:"center",
|
||||||
|
title:"调车时间",
|
||||||
|
key:"shuntSeconds",
|
||||||
|
width:120,
|
||||||
|
render(row: any) {
|
||||||
|
return formatWorkSeconds(row.shuntSeconds)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
align:"center",
|
||||||
|
title:"实际工时",
|
||||||
|
key:"actualWorkSeconds",
|
||||||
|
width:120,
|
||||||
|
render(row: any) {
|
||||||
|
return formatWorkSeconds(row.actualWorkSeconds)
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
align:"center",
|
align:"center",
|
||||||
title:"来源",
|
title:"来源",
|
||||||
@ -707,4 +720,114 @@ defineExpose({
|
|||||||
.pgClass {
|
.pgClass {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
:deep(.assing-work-table .n-data-table-th) {
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
:deep(.assing-work-table .n-data-table-td) {
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
:deep(.route-code-link) {
|
||||||
|
color: #1f6feb;
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
:deep(.route-code-link:hover) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
:deep(.cell-inline) {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.cell-main) {
|
||||||
|
color: #1f2328;
|
||||||
|
font-weight: 600;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
:deep(.cell-sub) {
|
||||||
|
color: #656d76;
|
||||||
|
font-size: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
:deep(.cell-full) {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: visible;
|
||||||
|
word-break: keep-all;
|
||||||
|
}
|
||||||
|
:deep(.n-data-table-td.material-full-cell) {
|
||||||
|
overflow: visible;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.action-cell) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.flag-group) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
:deep(.flag-chip) {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
:deep(.flag-chip.is-yes) {
|
||||||
|
color: #1a7f37;
|
||||||
|
background: #dafbe1;
|
||||||
|
}
|
||||||
|
:deep(.flag-chip.is-no) {
|
||||||
|
color: #656d76;
|
||||||
|
background: #f6f8fa;
|
||||||
|
}
|
||||||
|
:deep(.flag-chip.is-empty) {
|
||||||
|
color: #8c959f;
|
||||||
|
background: #f6f8fa;
|
||||||
|
}
|
||||||
|
:deep(.qty-text) {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2328;
|
||||||
|
}
|
||||||
|
:deep(.time-text) {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: #424a53;
|
||||||
|
}
|
||||||
|
:deep(.hours-cell) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #656d76;
|
||||||
|
}
|
||||||
|
:deep(.hours-item b) {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #424a53;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
:deep(.hours-item.is-actual),
|
||||||
|
:deep(.hours-item.is-actual b) {
|
||||||
|
color: #1f6feb;
|
||||||
|
}
|
||||||
|
:deep(.hours-item.is-unit),
|
||||||
|
:deep(.hours-item.is-unit b) {
|
||||||
|
color: #0d9488;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,46 +1,38 @@
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-card>
|
<div class="info-panel">
|
||||||
<n-card style="margin: 12px 0px">
|
<n-descriptions
|
||||||
<n-grid :cols="2" style="margin: 10px">
|
:column="2"
|
||||||
<n-gi>
|
bordered
|
||||||
<span class="pgClass">物料名称:</span>
|
size="small"
|
||||||
<span>{{props.baseInfo.materialName}}</span>
|
label-placement="left"
|
||||||
</n-gi>
|
class="info-header"
|
||||||
<n-gi>
|
>
|
||||||
<span class="pgClass">物料编号:</span>
|
<n-descriptions-item label="物料编码">{{ dash(props.baseInfo?.materialCode) }}</n-descriptions-item>
|
||||||
<span>{{props.baseInfo.materialCode}}</span>
|
<n-descriptions-item label="物料名称">{{ dash(props.baseInfo?.materialName) }}</n-descriptions-item>
|
||||||
</n-gi>
|
<n-descriptions-item label="工序名称">{{ dash(props.baseInfo?.processName) }}</n-descriptions-item>
|
||||||
</n-grid>
|
<n-descriptions-item label="派工编号">{{ dash(props.baseInfo?.assingCode) }}</n-descriptions-item>
|
||||||
<n-grid :cols="2" style="margin: 10px">
|
</n-descriptions>
|
||||||
<n-gi>
|
|
||||||
<span class="pgClass">工序名称:</span>
|
|
||||||
<span>{{props.baseInfo.processName}}</span>
|
|
||||||
</n-gi>
|
|
||||||
<n-gi>
|
|
||||||
<span class="pgClass">派工编号:</span>
|
|
||||||
<span>{{props.baseInfo.assingCode}}</span>
|
|
||||||
</n-gi>
|
|
||||||
</n-grid>
|
|
||||||
</n-card>
|
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="reportRecordsColums"
|
:columns="reportRecordsColums"
|
||||||
size="small"
|
:data="qualityTestingList"
|
||||||
:data="qualityTestingList"
|
:loading="loading"
|
||||||
remote
|
size="small"
|
||||||
:scroll-x="600"
|
striped
|
||||||
|
:single-line="true"
|
||||||
|
:row-key="(row: QualityTesting) => row.id ?? row.qcNo ?? ''"
|
||||||
/>
|
/>
|
||||||
<div class="pagination-container" style="display: flex; justify-content: flex-end; margin-top: 12px">
|
|
||||||
|
<div class="pagination-container">
|
||||||
<n-pagination
|
<n-pagination
|
||||||
v-model:page="pagination.page"
|
v-model:page="pagination.page"
|
||||||
v-model:page-size="pagination.pageSize"
|
v-model:page-size="pagination.pageSize"
|
||||||
:item-count="pagination.itemCount"
|
:item-count="pagination.itemCount"
|
||||||
:page-sizes="[2,10, 20, 50, 100]"
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
show-size-picker
|
show-size-picker
|
||||||
show-quick-jumper
|
show-quick-jumper
|
||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
@update:page-size="handlePageSizeChange"
|
@update:page-size="handlePageSizeChange"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
共 {{ pagination.itemCount }} 条
|
共 {{ pagination.itemCount }} 条
|
||||||
@ -48,126 +40,172 @@
|
|||||||
</n-pagination>
|
</n-pagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--质检详情弹窗-->
|
<div v-if="showDetail" class="detail-panel">
|
||||||
<n-modal v-model:show="showAssingWorkDetail" title="派工详情" style="width: 1000px" >
|
<div class="detail-head">
|
||||||
<n-card hoverable>
|
<span>质检明细<span v-if="currentQcNo" class="detail-no">{{ currentQcNo }}</span></span>
|
||||||
<template #header>
|
<n-button text size="small" @click="showDetail = false">收起</n-button>
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;width:100%">
|
</div>
|
||||||
<span>明细列表</span>
|
<n-data-table
|
||||||
<n-button text @click="detailIsCollapse = !detailIsCollapse">
|
:columns="detailColumns"
|
||||||
<n-icon :component="detailIsCollapse ? ArrowDownOutline : ArrowUpOutline" />
|
:data="detailTableData"
|
||||||
</n-button>
|
size="small"
|
||||||
</div>
|
striped
|
||||||
</template>
|
:single-line="true"
|
||||||
<n-data-table
|
:row-key="(row: any) => row.id"
|
||||||
v-show="!detailIsCollapse"
|
:max-height="280"
|
||||||
:columns="detailColumns"
|
/>
|
||||||
:data="detailTableData"
|
</div>
|
||||||
:row-key="(row:any) => row.id"
|
</div>
|
||||||
:scroll-x="700"
|
|
||||||
max-height="320"
|
|
||||||
:pagination="{ pageSize:4 }"
|
|
||||||
/>
|
|
||||||
</n-card>
|
|
||||||
</n-modal>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</n-card>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {NCard, NDataTable, NGi, NTag, NGrid, DataTableColumns, NIcon, NButton} from "naive-ui";
|
import { NDataTable, NTag, NDescriptions, NDescriptionsItem, NPagination, NButton, type DataTableColumns } from 'naive-ui'
|
||||||
import {onMounted, ref, h, reactive} from "vue";
|
import { onMounted, ref, h, reactive } from 'vue'
|
||||||
import { dictDataApi } from '@/api/org'
|
import { dictDataApi } from '@/api/org'
|
||||||
|
import { QualityTesting, qualityTestingApi } from '@/api/qualityTesting.ts'
|
||||||
|
import type { QcResultDetail } from '@/api/qcResultDetail.ts'
|
||||||
|
|
||||||
import {QualityTesting,qualityTestingApi} from "@/api/qualityTesting.ts";
|
const props = defineProps<{
|
||||||
import {ArrowDownOutline, ArrowUpOutline} from "@vicons/ionicons5";
|
assingId?: number
|
||||||
import type {QcResultDetail} from "@/api/qcResultDetail.ts";
|
baseInfo?: Record<string, any>
|
||||||
|
}>()
|
||||||
|
|
||||||
|
function dash(value: unknown) {
|
||||||
|
if (value === 0) return '0'
|
||||||
|
if (value == null || value === '') return '-'
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
const props =defineProps(
|
|
||||||
{
|
|
||||||
assingId:Object,
|
|
||||||
baseInfo:Object
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const pagination = reactive({
|
const pagination = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
itemCount: 0
|
itemCount: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const qualityTestingList = ref<QualityTesting[]>([])
|
||||||
|
const showDetail = ref(false)
|
||||||
|
const currentQcNo = ref('')
|
||||||
|
const detailTableData = ref<QcResultDetail[]>([])
|
||||||
|
|
||||||
//业务来源字典
|
const qcSourceTypeOptions = ref<{ label: string; value: any; class: any }[]>([])
|
||||||
const qcSourceTypeOptions = ref<{ label: string; value: any;class:any }[]>([])
|
const qcStatusOptions = ref<{ label: string; value: any; class: any }[]>([])
|
||||||
const qcStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
|
const qcResultTypeOptions = ref<{ label: string; value: any; class: any }[]>([])
|
||||||
|
const qcActionStatusOptions = ref<{ label: string; value: any; class: any }[]>([])
|
||||||
|
|
||||||
//汇报信息表格
|
const reportRecordsColums: DataTableColumns<QualityTesting> = [
|
||||||
const reportRecordsColums:DataTableColumns<QualityTesting> = [
|
{
|
||||||
|
title: '质检编号',
|
||||||
{ title: '质检编号', key: 'qcNo',align: 'center',
|
key: 'qcNo',
|
||||||
render(row:any) {
|
align: 'center',
|
||||||
|
render(row: any) {
|
||||||
return h(
|
return h(
|
||||||
'a',
|
'a',
|
||||||
{
|
{
|
||||||
class:"route-code-link",
|
class: 'route-code-link',
|
||||||
href: 'javascript:;',
|
href: 'javascript:;',
|
||||||
style:{
|
onClick: () => viewQualityResultDetail(row)
|
||||||
color: '#2080f0',
|
},
|
||||||
textDecoration: 'underline',
|
row.qcNo || '-'
|
||||||
cursor: 'pointer',
|
|
||||||
},
|
|
||||||
onClick: (e: MouseEvent) => {
|
|
||||||
viewQualityResultDetail(row)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
row.qcNo
|
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ title: '报检总数', key: 'totalQty',align: 'center' },
|
{ title: '报检总数', key: 'totalQty', align: 'center', width: 100, render: (row) => dash(row.totalQty) },
|
||||||
{ title: '已检验数量', key: 'inspectQty',align: 'center' },
|
{ title: '已检验数量', key: 'inspectQty', align: 'center', width: 110, render: (row) => dash(row.inspectQty) },
|
||||||
{ title: '质检状态', key: 'status',align: 'center',
|
{
|
||||||
|
title: '质检状态',
|
||||||
|
key: 'status',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
render(row) {
|
render(row) {
|
||||||
const val = row.status
|
const val = row.status
|
||||||
const opt = qcStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
const opt = qcStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
||||||
if (!opt) return val ?? '-'
|
if (!opt) return dash(val)
|
||||||
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
|
return h(NTag, { type: opt.class, size: 'small', bordered: false }, { default: () => opt.label })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ title: '检验完成时间', key: 'inspectTime',align: 'center' },
|
{ title: '检验完成时间', key: 'inspectTime', align: 'center', width: 170, render: (row) => dash(row.inspectTime) }
|
||||||
|
]
|
||||||
|
|
||||||
|
const detailColumns: DataTableColumns<QcResultDetail> = [
|
||||||
|
{
|
||||||
|
title: '判定类型',
|
||||||
|
key: 'resultType',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (row) => {
|
||||||
|
const val = row.resultType
|
||||||
|
const opt = qcResultTypeOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
||||||
|
if (!opt) return dash(val)
|
||||||
|
return h(NTag, { type: opt.class, size: 'small', bordered: false }, { default: () => opt.label })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '判定数量', key: 'qty', align: 'center', width: 90, render: (row) => dash(row.qty) },
|
||||||
|
{ title: '批次号', key: 'traceCode', align: 'center', render: (row) => dash(row.traceCode) },
|
||||||
|
{ title: '不良代码', key: 'defectCode', align: 'center', render: (row) => dash(row.defectCode) },
|
||||||
|
{ title: '后续动作', key: 'handleAction', align: 'center', render: (row) => dash(row.handleAction) },
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'actionStatus',
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
render: (row) => {
|
||||||
|
const val = row.actionStatus
|
||||||
|
const opt = qcActionStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
||||||
|
if (!opt) return dash(val)
|
||||||
|
return h(NTag, { type: opt.class, size: 'small', bordered: false }, { default: () => opt.label })
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 加载字典选项
|
|
||||||
async function loadDictOptions() {
|
async function loadDictOptions() {
|
||||||
try {
|
try {
|
||||||
const data = await dictDataApi.listByType('source_type')
|
const data = await dictDataApi.listByType('source_type')
|
||||||
qcSourceTypeOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class: d.listClass }))
|
qcSourceTypeOptions.value = data.map(d => ({
|
||||||
}catch {}
|
label: d.dictLabel,
|
||||||
|
value: Number(d.dictValue) || d.dictValue,
|
||||||
|
class: d.listClass
|
||||||
|
}))
|
||||||
|
} catch {}
|
||||||
try {
|
try {
|
||||||
const data = await dictDataApi.listByType('qc_status')
|
const data = await dictDataApi.listByType('qc_status')
|
||||||
qcStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class: d.listClass }))
|
qcStatusOptions.value = data.map(d => ({
|
||||||
}catch {}
|
label: d.dictLabel,
|
||||||
|
value: Number(d.dictValue) || d.dictValue,
|
||||||
|
class: d.listClass
|
||||||
|
}))
|
||||||
|
} catch {}
|
||||||
try {
|
try {
|
||||||
const data = await dictDataApi.listByType('qc_result_type')
|
const data = await dictDataApi.listByType('qc_result_type')
|
||||||
qcResultTypeOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class: d.listClass }))
|
qcResultTypeOptions.value = data.map(d => ({
|
||||||
}catch {}
|
label: d.dictLabel,
|
||||||
|
value: Number(d.dictValue) || d.dictValue,
|
||||||
|
class: d.listClass
|
||||||
|
}))
|
||||||
|
} catch {}
|
||||||
try {
|
try {
|
||||||
const data = await dictDataApi.listByType('qc_action_status')
|
const data = await dictDataApi.listByType('qc_action_status')
|
||||||
qcActionStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class: d.listClass }))
|
qcActionStatusOptions.value = data.map(d => ({
|
||||||
}catch {}
|
label: d.dictLabel,
|
||||||
|
value: Number(d.dictValue) || d.dictValue,
|
||||||
|
class: d.listClass
|
||||||
|
}))
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const qualityTestingList = ref<QualityTesting[]>([])
|
|
||||||
async function loadQualityTestingList() {
|
async function loadQualityTestingList() {
|
||||||
const res = await qualityTestingApi.loadQualityTestingList({
|
loading.value = true
|
||||||
assingId:props.assingId,
|
try {
|
||||||
page: pagination.page,
|
const res = await qualityTestingApi.loadQualityTestingList({
|
||||||
|
assingId: props.assingId,
|
||||||
|
page: pagination.page,
|
||||||
pageSize: pagination.pageSize
|
pageSize: pagination.pageSize
|
||||||
})
|
})
|
||||||
qualityTestingList.value = res.list
|
qualityTestingList.value = res.list ?? []
|
||||||
pagination.itemCount = res.total
|
pagination.itemCount = res.total ?? 0
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePageChange(page: number) {
|
function handlePageChange(page: number) {
|
||||||
@ -181,69 +219,56 @@ function handlePageSizeChange(pageSize: number) {
|
|||||||
loadQualityTestingList()
|
loadQualityTestingList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const showAssingWorkDetail = ref<Boolean>(false)
|
function viewQualityResultDetail(row: any) {
|
||||||
const detailIsCollapse = ref(false)
|
showDetail.value = true
|
||||||
|
currentQcNo.value = row.qcNo || ''
|
||||||
const detailColumns: DataTableColumns<QcResultDetail> = [
|
detailTableData.value = row.qcResultDetailList ?? []
|
||||||
{
|
|
||||||
title: '判定类型',
|
|
||||||
key: 'resultType',
|
|
||||||
align: 'center',
|
|
||||||
width: 80,
|
|
||||||
render: (row) => {
|
|
||||||
const val = row.resultType
|
|
||||||
const opt = qcResultTypeOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
|
||||||
if (!opt) return val ?? '-'
|
|
||||||
|
|
||||||
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '判定数量',
|
|
||||||
key: 'qty',
|
|
||||||
align: 'center',
|
|
||||||
width: 80
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '批次号',
|
|
||||||
key: 'traceCode',
|
|
||||||
width: 120,
|
|
||||||
align: 'center',
|
|
||||||
render: (row) => {
|
|
||||||
const val = row.traceCode
|
|
||||||
return val ?? '-'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
key: 'actionStatus',
|
|
||||||
width: 80,
|
|
||||||
align: 'center',
|
|
||||||
render: (row) => {
|
|
||||||
const val = row.actionStatus
|
|
||||||
const opt = qcActionStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
|
||||||
if (!opt) return val ?? '-'
|
|
||||||
|
|
||||||
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const detailTableData = ref<Object[]>([])
|
|
||||||
const qcResultTypeOptions = ref<{ label: string; value: any;class:any }[]>([])
|
|
||||||
//动作执行状态
|
|
||||||
const qcActionStatusOptions = ref<{ label: string; value: any;class:any }[]>([])
|
|
||||||
|
|
||||||
function viewQualityResultDetail(row) {
|
|
||||||
showAssingWorkDetail.value = true
|
|
||||||
detailTableData.value = row.qcResultDetailList
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(() => {
|
||||||
loadDictOptions()
|
loadDictOptions()
|
||||||
loadQualityTestingList()
|
loadQualityTestingList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
<style scoped>
|
||||||
|
.info-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.info-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.detail-panel {
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #eaeef2;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #f8fafc;
|
||||||
|
}
|
||||||
|
.detail-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2328;
|
||||||
|
}
|
||||||
|
.detail-no {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #1f6feb;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
:deep(.route-code-link) {
|
||||||
|
color: #1f6feb;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
:deep(.route-code-link:hover) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,71 +1,69 @@
|
|||||||
|
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-card>
|
<div class="info-panel">
|
||||||
<n-card style="margin: 12px 0px">
|
<n-descriptions
|
||||||
<n-grid :cols="2" style="margin: 10px">
|
:column="2"
|
||||||
<n-gi>
|
bordered
|
||||||
<span class="pgClass">物料名称:</span>
|
size="small"
|
||||||
<span>{{props.baseInfo.materialName}}</span>
|
label-placement="left"
|
||||||
</n-gi>
|
class="info-header"
|
||||||
<n-gi>
|
>
|
||||||
<span class="pgClass">物料编号:</span>
|
<n-descriptions-item label="物料编码">{{ dash(props.baseInfo?.materialCode) }}</n-descriptions-item>
|
||||||
<span>{{props.baseInfo.materialCode}}</span>
|
<n-descriptions-item label="物料名称">{{ dash(props.baseInfo?.materialName) }}</n-descriptions-item>
|
||||||
</n-gi>
|
<n-descriptions-item label="工序名称">{{ dash(props.baseInfo?.processName) }}</n-descriptions-item>
|
||||||
</n-grid>
|
<n-descriptions-item label="派工编号">{{ dash(props.baseInfo?.assingCode) }}</n-descriptions-item>
|
||||||
<n-grid :cols="2" style="margin: 10px">
|
</n-descriptions>
|
||||||
<n-gi>
|
|
||||||
<span class="pgClass">工序名称:</span>
|
|
||||||
<span>{{props.baseInfo.processName}}</span>
|
|
||||||
</n-gi>
|
|
||||||
<n-gi>
|
|
||||||
<span class="pgClass">派工编号:</span>
|
|
||||||
<span>{{props.baseInfo.assingCode}}</span>
|
|
||||||
</n-gi>
|
|
||||||
</n-grid>
|
|
||||||
</n-card>
|
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="reportRecordsColums"
|
:columns="reportRecordsColums"
|
||||||
size="small"
|
:data="submitLogList"
|
||||||
:data="submitLogList"
|
:loading="loading"
|
||||||
remote
|
size="small"
|
||||||
:scroll-x="600"
|
striped
|
||||||
|
:single-line="true"
|
||||||
|
:scroll-x="1080"
|
||||||
|
:row-key="(row: SubmitLog) => row.id ?? row.createTime ?? ''"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="pagination-container" style="display: flex; justify-content: flex-end; margin-top: 12px">
|
<div class="pagination-container">
|
||||||
<n-pagination
|
<n-pagination
|
||||||
v-model:page="pagination.page"
|
v-model:page="pagination.page"
|
||||||
v-model:page-size="pagination.pageSize"
|
v-model:page-size="pagination.pageSize"
|
||||||
:item-count="pagination.itemCount"
|
:item-count="pagination.itemCount"
|
||||||
:page-sizes="[2,10, 20, 50, 100]"
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
show-size-picker
|
show-size-picker
|
||||||
show-quick-jumper
|
show-quick-jumper
|
||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
@update:page-size="handlePageSizeChange"
|
@update:page-size="handlePageSizeChange"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
共 {{ pagination.itemCount }} 条
|
共 {{ pagination.itemCount }} 条
|
||||||
</template>
|
</template>
|
||||||
</n-pagination>
|
</n-pagination>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NCard, NDataTable, NGi,NTag, NGrid,DataTableColumns} from "naive-ui";
|
import { NDataTable, NTag, NDescriptions, NDescriptionsItem, NPagination, type DataTableColumns } from 'naive-ui'
|
||||||
import {onMounted, ref, h, reactive} from "vue";
|
import { onMounted, ref, h, reactive } from 'vue'
|
||||||
import { dictDataApi } from '@/api/org'
|
import { dictDataApi } from '@/api/org'
|
||||||
import { SubmitLog,submitLogApi} from '@/api/submitLog'
|
import { SubmitLog, submitLogApi } from '@/api/submitLog'
|
||||||
|
import { formatWorkSeconds } from '@/utils/tool'
|
||||||
|
|
||||||
const props =defineProps(
|
const props = defineProps<{
|
||||||
{
|
assingId?: number
|
||||||
assingId:Number,
|
baseInfo?: Record<string, any>
|
||||||
baseInfo:Object
|
}>()
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//汇报状态字典
|
function dash(value: unknown) {
|
||||||
const submitStatusOptions = ref<{ label: string; value: any ;class:any}[]>([])
|
if (value === 0) return '0'
|
||||||
const reportTypeOptions = ref<{ label: string; value: any ;class:any}[]>([])
|
if (value == null || value === '') return '-'
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitStatusOptions = ref<{ label: string; value: any; class: any }[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const submitLogList = ref<SubmitLog[]>([])
|
||||||
|
|
||||||
const pagination = reactive({
|
const pagination = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -73,46 +71,71 @@ const pagination = reactive({
|
|||||||
itemCount: 0
|
itemCount: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
//汇报信息表格
|
const reportRecordsColums: DataTableColumns<SubmitLog> = [
|
||||||
const reportRecordsColums:DataTableColumns<SubmitLog> = [
|
{ title: '汇报数量', key: 'quantity', align: 'center', width: 90, render: (row) => dash(row.quantity) },
|
||||||
{ title: '汇报数量', key: 'quantity',align: 'center'},
|
{ title: '合格数量', key: 'qualifiedQty', align: 'center', width: 90, render: (row: any) => dash(row.qualifiedQty) },
|
||||||
{ title: '合格数量', key: 'qualifiedQty',align: 'center'},
|
{ title: '报废数量', key: 'scrapQty', align: 'center', width: 90, render: (row: any) => dash(row.scrapQty) },
|
||||||
{ title: '报废数量', key: 'scrapeQty',align: 'center'},
|
{
|
||||||
{ title: '汇报人', key: 'userName',align: 'center' },
|
title: '暂停时间',
|
||||||
{ title: '状态', key: 'submitStatus',align: 'center',
|
key: 'pauseSeconds',
|
||||||
render: (row) => {
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
render: (row: any) => formatWorkSeconds(row.pauseSeconds)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '调车时间',
|
||||||
|
key: 'shuntSeconds',
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
render: (row: any) => formatWorkSeconds(row.shuntSeconds)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '实际工时',
|
||||||
|
key: 'actualWorkSeconds',
|
||||||
|
align: 'center',
|
||||||
|
width: 120,
|
||||||
|
render: (row: any) => h('span', { class: 'hours-actual' }, formatWorkSeconds(row.actualWorkSeconds))
|
||||||
|
},
|
||||||
|
{ title: '汇报人', key: 'userName', align: 'center', width: 100, render: (row: any) => dash(row.userName) },
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'submitStatus',
|
||||||
|
align: 'center',
|
||||||
|
width: 90,
|
||||||
|
render: (row: any) => {
|
||||||
const val = row.submitStatus
|
const val = row.submitStatus
|
||||||
const opt = submitStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
const opt = submitStatusOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
||||||
if (!opt) return val ?? '-'
|
if (!opt) return dash(val)
|
||||||
|
return h(NTag, { type: opt.class, size: 'small', bordered: false }, { default: () => opt.label })
|
||||||
return h(NTag, { type: opt.class, size: 'small' }, { default: () => opt.label })
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ title: '汇报时间', key: 'createTime',align: 'center' },
|
{ title: '汇报时间', key: 'createTime', align: 'center', width: 160, render: (row) => dash(row.createTime) }
|
||||||
]
|
]
|
||||||
|
|
||||||
// 加载字典选项
|
|
||||||
async function loadDictOptions() {
|
async function loadDictOptions() {
|
||||||
try {
|
try {
|
||||||
const data = await dictDataApi.listByType('submit_status')
|
const data = await dictDataApi.listByType('submit_status')
|
||||||
submitStatusOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class:d.listClass }))
|
submitStatusOptions.value = data.map(d => ({
|
||||||
}catch {}
|
label: d.dictLabel,
|
||||||
try {
|
value: Number(d.dictValue) || d.dictValue,
|
||||||
const data = await dictDataApi.listByType('qc_result_type')
|
class: d.listClass
|
||||||
reportTypeOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue),class:d.listClass }))
|
}))
|
||||||
}catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
//加载汇报信息
|
|
||||||
const submitLogList = ref<SubmitLog[]>([])
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const res = await submitLogApi.loadSumbitLog({
|
loading.value = true
|
||||||
assingId:props.assingId,
|
try {
|
||||||
page: pagination.page,
|
const res = await submitLogApi.loadSumbitLog({
|
||||||
pageSize: pagination.pageSize
|
assingId: props.assingId ?? 0,
|
||||||
})
|
page: pagination.page,
|
||||||
submitLogList.value = res.list
|
pageSize: pagination.pageSize
|
||||||
pagination.itemCount = res.total
|
})
|
||||||
|
submitLogList.value = res.list ?? []
|
||||||
|
pagination.itemCount = res.total ?? 0
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePageChange(page: number) {
|
function handlePageChange(page: number) {
|
||||||
@ -126,11 +149,28 @@ function handlePageSizeChange(pageSize: number) {
|
|||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(()=>{
|
onMounted(() => {
|
||||||
loadDictOptions()
|
loadDictOptions()
|
||||||
load()
|
load()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
<style scoped>
|
||||||
|
.info-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.info-header {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
:deep(.hours-actual) {
|
||||||
|
color: #1f6feb;
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user