diff --git a/package-lock.json b/package-lock.json index 8fa9133..bee8871 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,8 @@ "naive-ui": "^2.37.3", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.1", + "qrcode": "^1.5.4", + "seemly": "^0.3.10", "three": "^0.184.0", "vue": "^3.4.15", "vue-draggable-plus": "^0.6.1", @@ -30,6 +32,7 @@ }, "devDependencies": { "@types/node": "^20.11.5", + "@types/qrcode": "^1.5.6", "@types/three": "^0.184.1", "@vitejs/plugin-vue": "^5.0.3", "electron": "^42.5.0", @@ -1739,6 +1742,16 @@ "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", "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": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", @@ -2043,7 +2056,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2053,7 +2065,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2502,6 +2513,15 @@ "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": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", @@ -2620,7 +2640,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2633,7 +2652,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "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": { "version": "6.0.0", "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", "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": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/dingtalk-jsapi/-/dingtalk-jsapi-3.2.9.tgz", @@ -3242,7 +3275,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/end-of-stream": { @@ -3503,6 +3535,19 @@ "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": { "version": "1.15.11", "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -3589,7 +3634,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -3980,7 +4024,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4169,6 +4212,18 @@ "dev": true, "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": { "version": "4.17.23", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz", @@ -4596,6 +4651,42 @@ "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": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", @@ -4619,6 +4710,15 @@ "dev": true, "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -4764,6 +4864,15 @@ "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": { "version": "8.5.6", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", @@ -4925,6 +5034,89 @@ "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": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -5002,7 +5194,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5018,6 +5209,12 @@ "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": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", @@ -5247,6 +5444,12 @@ "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": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5362,7 +5565,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -5377,7 +5579,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -5922,6 +6123,12 @@ "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": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index e19a9b8..9b4b94e 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "naive-ui": "^2.37.3", "pinia": "^2.1.7", "pinia-plugin-persistedstate": "^3.2.1", + "qrcode": "^1.5.4", "seemly": "^0.3.10", "three": "^0.184.0", "vue": "^3.4.15", @@ -35,6 +36,7 @@ }, "devDependencies": { "@types/node": "^20.11.5", + "@types/qrcode": "^1.5.6", "@types/three": "^0.184.1", "@vitejs/plugin-vue": "^5.0.3", "electron": "^42.5.0", diff --git a/src/api/AssingWorkDetail.ts b/src/api/AssingWorkDetail.ts index 81c4598..e4747ad 100644 --- a/src/api/AssingWorkDetail.ts +++ b/src/api/AssingWorkDetail.ts @@ -16,6 +16,10 @@ export interface AssingWorkDetail { createTime?: string + pauseSeconds?: number + shuntSeconds?: number + actualWorkSeconds?: number + } // AssingWorkDetail API diff --git a/src/api/orderProcessPlan.ts b/src/api/orderProcessPlan.ts index ec44bb7..9c83f07 100644 --- a/src/api/orderProcessPlan.ts +++ b/src/api/orderProcessPlan.ts @@ -349,6 +349,20 @@ export const orderProcessPlanApi = { 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 }>({ diff --git a/src/api/outsourcing.ts b/src/api/outsourcing.ts index be639fd..163ef94 100644 --- a/src/api/outsourcing.ts +++ b/src/api/outsourcing.ts @@ -30,6 +30,21 @@ export interface Outsourcing { issuedQuantity?: number | string receivedQuantity?: 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 = { @@ -67,6 +82,14 @@ export const outsourcingApi = { return request({ url: `/biz/outsourcing/${id}`, method: 'get' }) }, + details(id: number | string) { + return request({ url: `/biz/outsourcing/${id}/details`, method: 'get' }) + }, + + pushKingdee(id: number | string) { + return request({ url: `/biz/outsourcing/${id}/pushKingdee`, method: 'post' }) + }, + create(data: Outsourcing) { return request({ url: '/biz/outsourcing', method: 'post', data }) }, diff --git a/src/api/production.ts b/src/api/production.ts index 7037443..c13afad 100644 --- a/src/api/production.ts +++ b/src/api/production.ts @@ -16,6 +16,10 @@ export interface AssingWork { status:number, reason:string, pauseTime:Date, + pauseSeconds?: number, + shuntSeconds?: number, + actualWorkSeconds?: number, + unitWorkSeconds?: number, assingStatus:number, assingCode:string, parentId:number, diff --git a/src/api/submitLog.ts b/src/api/submitLog.ts index 80f2d8d..56f486e 100644 --- a/src/api/submitLog.ts +++ b/src/api/submitLog.ts @@ -24,6 +24,10 @@ export interface SubmitLog { remark?: string + pauseSeconds?: number + shuntSeconds?: number + actualWorkSeconds?: number + } // 派工工单汇报记录 API diff --git a/src/utils/tool.ts b/src/utils/tool.ts index 6059fc4..c09d090 100644 --- a/src/utils/tool.ts +++ b/src/utils/tool.ts @@ -12,4 +12,16 @@ export function getaDtetime(timestamp:any) { 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}秒` } \ No newline at end of file diff --git a/src/views/biz/orderProcessPlan/board.vue b/src/views/biz/orderProcessPlan/board.vue index 6c0a202..ee39a70 100644 --- a/src/views/biz/orderProcessPlan/board.vue +++ b/src/views/biz/orderProcessPlan/board.vue @@ -1,36 +1,36 @@ @@ -684,7 +714,8 @@ import {ref, reactive, onMounted, h, computed, type Component, toRaw} from 'vue' import { NButton, NSpace, NIcon, NTag, NSpin, NModal, NForm, NFormItem, NGrid, NGi, 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, } from 'naive-ui' import { @@ -706,9 +737,11 @@ import { type OutsourcingProcessOption, OrderProcessPlan, } from '@/api/orderProcessPlan' import { useUserStore } from '@/stores/user' +import { deptApi } from '@/api/org' import ProcessFlowStepper from './components/ProcessFlowStepper.vue' import ProcessCard from './components/ProcessCard.vue' import SplitProcessDialog from './components/SplitProcessDialog.vue' +import WorkOrderPrintPreview from './components/WorkOrderPrintPreview.vue' import PlmModelDrawer from '@/components/PlmModelDrawer.vue' import type { PlmModelOpenContext } from '@/api/plmModel' import Pgitem from './pgitem.vue' @@ -723,10 +756,14 @@ const userStore = useUserStore() const hasPermission = (p: string) => userStore.hasPermission(p) const searchForm = reactive({ - name: '', - code: '', + mainCode: '', + materialCode: '', + materialName: '', + workshopId: null as number | null, }) +const workshopOptions = ref<{ label: string; value: number }[]>([]) + const tableData = ref([]) const expandedIds = ref>(new Set()) const selectedOrderIds = ref([]) @@ -737,6 +774,10 @@ const detailVisible = ref(false) const detailLoading = ref(false) const detailData = ref(null) +const printVisible = ref(false) +const printLoading = ref(false) +const printData = ref(null) + /** 工序工单拆单 */ const splitVisible = ref(false) const splitPlanId = ref(null) @@ -793,7 +834,6 @@ const outsourceForm = reactive({ remark: '', }) const outsourceRules: FormRules = { - supplierName: [{ required: true, message: '请输入供应商', trigger: 'blur' }], quantity: [{ required: true, type: 'number', message: '请输入委外数量', trigger: ['blur', 'change'] }], } @@ -1031,8 +1071,10 @@ async function loadData() { const res = await orderProcessPlanApi.page({ page: pagination.page, pageSize: pagination.pageSize, - name: searchForm.name || undefined, - code: searchForm.code || undefined, + mainCode: searchForm.mainCode || undefined, + materialCode: searchForm.materialCode || undefined, + materialName: searchForm.materialName || undefined, + workshopId: searchForm.workshopId ?? undefined, }) tableData.value = res.list ?? [] pagination.itemCount = res.total @@ -1047,8 +1089,10 @@ function handleSearch() { } function handleReset() { - searchForm.name = '' - searchForm.code = '' + searchForm.mainCode = '' + searchForm.materialCode = '' + searchForm.materialName = '' + searchForm.workshopId = null handleSearch() } @@ -1072,6 +1116,10 @@ function onBatchSelect(key: string) { } function onMoreSelect(key: string, order: OrderProcessPlanVO) { + if (key === 'print') { + openPrintPreview(order) + return + } if (key === 'createOutsource') { openOutsource(order) return @@ -1086,7 +1134,6 @@ function onMoreSelect(key: string, order: OrderProcessPlanVO) { } const labels: Record = { - print: '打印', withdraw: '撤回', freeze: '冻结工单', delete: '删除', @@ -1181,14 +1228,14 @@ async function submitOutsource() { try { await orderProcessPlanApi.createOutsourcing({ processPlanId: outsourceSelectedPlanId.value, - supplierName: outsourceForm.supplierName.trim(), + supplierName: outsourceForm.supplierName.trim() || undefined, quantity: outsourceForm.quantity!, unitPrice: outsourceForm.unitPrice ?? undefined, planStartTime: formatSubmitDateTime(outsourceForm.planStartTime), planEndTime: formatSubmitDateTime(outsourceForm.planEndTime), remark: outsourceForm.remark || undefined, }) - message.success('委外单创建成功') + message.success(outsourceForm.supplierName.trim() ? '委外单创建成功' : '已创建草稿委外单,补齐供应商后可下推金蝶') outsourceModalVisible.value = false loadData() } 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) { if (!order.orderItemId) return detailVisible.value = true @@ -1404,10 +1469,7 @@ function dispatchSubmit() { } async function handleExport() { - const blob = await orderProcessPlanApi.export({ - name: searchForm.name || undefined, - code: searchForm.code || undefined, - }) + const blob = await orderProcessPlanApi.export({}) const url = window.URL.createObjectURL(blob) const link = document.createElement('a') 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() +}) ${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((resolve) => { + if (img.complete) resolve() + else { + img.onload = () => resolve() + img.onerror = () => resolve() + } + }), + ), + ).then(run) +} + + + diff --git a/src/views/biz/orderProcessPlan/components/workOrderPrint.css b/src/views/biz/orderProcessPlan/components/workOrderPrint.css new file mode 100644 index 0000000..313df5f --- /dev/null +++ b/src/views/biz/orderProcessPlan/components/workOrderPrint.css @@ -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; + } +} diff --git a/src/views/biz/outsourcing/index.vue b/src/views/biz/outsourcing/index.vue index 82a22c4..fbeb557 100644 --- a/src/views/biz/outsourcing/index.vue +++ b/src/views/biz/outsourcing/index.vue @@ -7,7 +7,13 @@ 新建工序委外 - 下推 + 下推金蝶 批量删除 @@ -58,7 +64,7 @@ :loading="loading" :pagination="pagination" :row-key="(row) => row.id!" - :scroll-x="1520" + :scroll-x="1720" @update:page="handlePageChange" @update:page-size="handlePageSizeChange" @update:checked-row-keys="handleCheck" @@ -82,8 +88,21 @@ {{ formatDecimal(detailData.qualifiedQuantity) }} {{ detailData.beginPreparationTime || '-' }} {{ detailData.endPreparationTime || '-' }} + {{ detailData.kingdeePushed === 1 ? '已下推' : '未下推' }} + {{ detailData.kingdeeBillNo || '-' }} {{ detailData.remark || '-' }} +
+
分批明细
+ +
@@ -97,10 +116,16 @@ - + - + @@ -202,11 +227,12 @@ const defaultFormData = (): Record => ({ beginPreparationTime: null, endPreparationTime: null, remark: '', + kingdeePushed: 0, + details: [], }) const formData = reactive>(defaultFormData()) const formRules: FormRules = { - supplierName: [{ required: true, message: '请输入供应商', trigger: 'blur' }], quantity: [{ required: true, type: 'number', message: '请输入委外数量', trigger: ['blur', 'change'] }], } @@ -358,18 +384,40 @@ const columns: DataTableColumns = [ ]) }, }, + { + 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: '操作', key: 'actions', - width: 120, + width: 168, align: 'center', fixed: 'right', render(row) { + const canPush = row.kingdeePushed !== 1 && !!row.supplierName return h('div', { class: 'cell-center' }, [ h(NSpace, { size: 4, justify: 'center' }, { 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', + disabled: !canPush, + onClick: () => handlePushKingdee(row.id), + }, { default: () => '下推' }), ], }), ]) @@ -377,6 +425,13 @@ const columns: DataTableColumns = [ }, ] +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() { loading.value = true try { @@ -440,13 +495,15 @@ async function openDetail(row: Outsourcing) { detailVisible.value = true } -function handleEdit(row: Outsourcing) { +async function handleEdit(row: Outsourcing) { + if (!row.id) return modalTitle.value = '编辑委外工单' - Object.assign(formData, defaultFormData(), row, { - quantity: row.quantity ?? null, - unitPrice: row.unitPrice != null ? Number(row.unitPrice) : null, - beginPreparationTime: parseDateTime(row.beginPreparationTime), - endPreparationTime: parseDateTime(row.endPreparationTime), + const full = await outsourcingApi.detail(row.id) + Object.assign(formData, defaultFormData(), full, { + quantity: full.quantity ?? null, + unitPrice: full.unitPrice != null ? Number(full.unitPrice) : null, + beginPreparationTime: parseDateTime(full.beginPreparationTime), + endPreparationTime: parseDateTime(full.endPreparationTime), }) 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() { dialog.warning({ title: '提示', diff --git a/src/views/production/componets/AssingWorkTreeTable.vue b/src/views/production/componets/AssingWorkTreeTable.vue index 291dbba..bed356f 100644 --- a/src/views/production/componets/AssingWorkTreeTable.vue +++ b/src/views/production/componets/AssingWorkTreeTable.vue @@ -1,19 +1,20 @@