diff --git a/frontend/src/components/documentForms/documentForms.test.js b/frontend/src/components/documentForms/documentForms.test.js index cbfa01f..1870d88 100644 --- a/frontend/src/components/documentForms/documentForms.test.js +++ b/frontend/src/components/documentForms/documentForms.test.js @@ -49,4 +49,21 @@ describe("document form foundation", () => { assert.match(mainCss, /\.document-settle-panel/); assert.match(mainCss, /\.warehouse-paper-control-grid/); }); + + it("keeps the final paper layout contract after legacy warehouse grid rules", () => { + const legacyWarehouseGrid = mainCss.lastIndexOf(".warehouse-document-section-grid {"); + const finalContract = mainCss.lastIndexOf("Final paper document layout contract"); + const warehousePaperGrid = mainCss.lastIndexOf("body .warehouse-document-section-grid > .warehouse-paper-grid"); + const genericWrappedWarehousePaperGrid = mainCss.lastIndexOf("body .document-section-grid > .warehouse-paper-grid"); + const genericWrappedWarehouseMeta = mainCss.lastIndexOf("body .document-section-grid > .warehouse-document-meta-strip"); + const genericDocumentGrid = mainCss.lastIndexOf("body .document-section-grid > .document-grid"); + + assert.ok(legacyWarehouseGrid > 0, "expected the legacy warehouse grid rule to exist"); + assert.ok(finalContract > legacyWarehouseGrid, "expected final paper contract after legacy grid rules"); + assert.ok(warehousePaperGrid > legacyWarehouseGrid, "expected warehouse paper grid full-width rule after legacy grid"); + assert.ok(genericWrappedWarehousePaperGrid > legacyWarehouseGrid, "expected generic section wrapped warehouse paper grid rule after legacy grid"); + assert.ok(genericWrappedWarehouseMeta > legacyWarehouseGrid, "expected generic section wrapped warehouse meta strip rule after legacy grid"); + assert.ok(genericDocumentGrid > 0, "expected generic document grid full-width rule in final contract"); + assert.match(mainCss.slice(finalContract), /grid-column: 1 \/ -1 !important;/); + }); }); diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css index 6e49bc5..4412898 100644 --- a/frontend/src/styles/main.css +++ b/frontend/src/styles/main.css @@ -16282,6 +16282,14 @@ body .login-production-layout .login-security-foot p { background: rgba(255, 250, 240, 0.5); } +.document-grid-cell-wide { + grid-column: span 2; +} + +.document-grid-cell-full { + grid-column: 1 / -1; +} + .document-grid-label, .document-grid-value { margin: 0; @@ -16789,11 +16797,21 @@ body .login-production-layout .login-security-foot p { padding: 14px; } +.document-section-grid > .document-grid, +.document-section-grid > .document-line-table, .document-section-grid > .document-control-grid, .document-section-grid > .document-selector-panel, .document-section-grid > .document-source-table, .document-section-grid > .document-inline-summary, .document-section-grid > .document-settle-panel, +.document-section-grid > .warehouse-document-meta-strip, +.document-section-grid > .warehouse-paper-grid, +.document-section-grid > .warehouse-paper-control-grid, +.document-section-grid > .warehouse-paper-source-lot-panel, +.document-section-grid > .warehouse-paper-source-lot-table, +.document-section-grid > .warehouse-paper-inline-summary, +.document-section-grid > .warehouse-paper-settle-panel, +.document-section-grid > .warehouse-paper-summary-grid, .document-section-grid > .document-paper-field { grid-column: 1 / -1; width: 100%; @@ -17036,6 +17054,11 @@ body .login-production-layout .login-security-foot p { grid-column: 1 / -1; } + .document-grid-cell-wide, + .document-grid-cell-full { + grid-column: 1 / -1; + } + .document-action-bar, .document-action-bar--with-state, .document-action-state, @@ -19250,7 +19273,56 @@ body .warehouse-document-action-bar { transform: none !important; } +/* Final paper document layout contract: later legacy drawer grids must not squeeze formal document blocks. */ +body .document-section-grid > .document-grid, +body .document-section-grid > .document-line-table, +body .document-section-grid > .document-control-grid, +body .document-section-grid > .document-selector-panel, +body .document-section-grid > .document-source-table, +body .document-section-grid > .document-inline-summary, +body .document-section-grid > .document-settle-panel, +body .document-section-grid > .document-paper-field, +body .document-section-grid > .warehouse-document-meta-strip, +body .document-section-grid > .warehouse-paper-grid, +body .document-section-grid > .warehouse-paper-control-grid, +body .document-section-grid > .warehouse-paper-source-lot-panel, +body .document-section-grid > .warehouse-paper-source-lot-table, +body .document-section-grid > .warehouse-paper-inline-summary, +body .document-section-grid > .warehouse-paper-settle-panel, +body .document-section-grid > .warehouse-paper-summary-grid, +body .warehouse-document-section-grid > .warehouse-document-meta-strip, +body .warehouse-document-section-grid > .warehouse-paper-grid, +body .warehouse-document-section-grid > .warehouse-paper-control-grid, +body .warehouse-document-section-grid > .warehouse-paper-source-lot-panel, +body .warehouse-document-section-grid > .warehouse-paper-source-lot-table, +body .warehouse-document-section-grid > .warehouse-paper-inline-summary, +body .warehouse-document-section-grid > .warehouse-paper-settle-panel, +body .warehouse-document-section-grid > .warehouse-paper-summary-grid { + grid-column: 1 / -1 !important; + width: 100% !important; + min-width: 0 !important; +} + +body .workflow-drawer.form-drawer:has(.document-form-shell) { + width: min(1280px, calc(100vw - 24px)) !important; +} + +body .workflow-drawer.form-drawer:has(.document-form-shell) .form-drawer-body { + overflow-x: hidden !important; +} + +body .document-action-bar { + position: relative !important; + right: auto !important; + bottom: auto !important; + z-index: 1 !important; + margin: 0 0 0 auto !important; + transform: none !important; +} + @media (max-width: 620px) { + body .document-action-bar, + body .document-action-bar--with-state, body .warehouse-document-action-bar, body .warehouse-document-action-bar--shared, body .warehouse-document-action-bar--shared.warehouse-document-action-bar--with-state { diff --git a/frontend/src/views/ReturnManagementView.test.js b/frontend/src/views/ReturnManagementView.test.js new file mode 100644 index 0000000..f6eaaa5 --- /dev/null +++ b/frontend/src/views/ReturnManagementView.test.js @@ -0,0 +1,50 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const source = readFileSync(join(__dirname, "ReturnManagementView.vue"), "utf8"); + +function drawerBlock(openBinding, nextMarker) { + const start = source.indexOf(`:open="${openBinding}"`); + assert.ok(start >= 0, `expected drawer ${openBinding} to exist`); + const end = source.indexOf(nextMarker, start + openBinding.length); + assert.ok(end > start, `expected drawer ${openBinding} to end before ${nextMarker}`); + return source.slice(start, end); +} + +describe("ReturnManagementView formal document layout", () => { + it("renders return registration as a formal paper document instead of a stack form", () => { + const block = drawerBlock("returnDrawerOpen", ':open="dispositionDrawerOpen"'); + + assert.match(block, //); + assert.doesNotMatch(block, /
{ + const block = drawerBlock("dispositionDrawerOpen", ':open="dispositionRecordsDrawerOpen"'); + + assert.match(block, //); + assert.doesNotMatch(block, / { + assert.match(source, /const returnDocumentHeaderFields = computed/); + assert.match(source, /const returnDocumentLineColumns = \[/); + assert.match(source, /const dispositionDocumentHeaderFields = computed/); + assert.match(source, /const dispositionDocumentLineColumns = \[/); + }); +}); diff --git a/frontend/src/views/ReturnManagementView.vue b/frontend/src/views/ReturnManagementView.vue index fc22fd2..7cb05a0 100644 --- a/frontend/src/views/ReturnManagementView.vue +++ b/frontend/src/views/ReturnManagementView.vue @@ -73,120 +73,116 @@ wide @close="returnDrawerOpen = false" > - -
- - -
- -
- - -
- - - -
- 退货明细 - -
- -
-
-
- 明细 {{ index + 1 }} - -
- - - -
- -
- - - -
- - + + + + + + + - - + + + + -
- + + + + + + + + + + + - + + + + 1 + {{ activeDispositionItem.return_no || "-" }} + {{ activeDispositionItem.product_name || "-" }} + {{ activeDispositionItem.source_lot_no || "-" }} + {{ formatQty(activeDispositionItem.return_qty) }} + {{ formatWeight(activeDispositionItem.return_weight_kg) }} + {{ activeDispositionItem.responsibility_type || "-" }} + + + 请选择待处置退货行 + + + - - -
- - -
- - - - - + + + +
{ } return pendingItems.value.filter((item) => item.return_order_id === Number(selectedReturnOrderId.value)); }); +const activeDispositionItem = computed(() => selectedDispositionItem()); const visibleDispositions = computed(() => { const order = activeReturnOrder.value; if (!order) { @@ -385,6 +407,48 @@ const dispositionDrawerColumns = computed(() => [ { key: "rework_work_order_no", label: "返工工单", detailOnly: true }, { key: "scrap_sale_amount", label: "废料销售金额", detailOnly: true, format: (value) => `¥${formatAmount(value)}` } ]); +const returnDocumentHeaderFields = computed(() => [ + { key: "customer", label: "客户" }, + { key: "sales_order", label: "订单" }, + { key: "delivery", label: "发货单" }, + { key: "handler", label: "处理人" }, + { key: "return_reason", label: "退货原因", class: "document-grid-cell-wide" } +]); +const returnDocumentLineColumns = [ + { key: "index", label: "序号", width: "58px" }, + { key: "sales_order_item", label: "订单行", width: "22%" }, + { key: "product", label: "产品", width: "20%" }, + { key: "source_lot", label: "来源批次", width: "18%" }, + { key: "return_qty", label: "退货数量", width: "104px" }, + { key: "return_weight_kg", label: "退货重量(kg)", width: "126px" }, + { key: "responsibility_type", label: "责任归属", width: "132px" }, + { key: "actions", label: "操作", width: "92px" } +]; +const dispositionDocumentHeaderFields = computed(() => { + const fields = [ + { key: "return_item", label: "待处置退货行", class: "document-grid-cell-wide" }, + { key: "disposition_type", label: "处置方式" } + ]; + if (dispositionForm.disposition_type === "SCRAP") { + fields.push( + { key: "scrap_weight_kg", label: "报废重量(kg)" }, + { key: "scrap_sale_amount", label: "废料销售金额" } + ); + } else { + fields.push({ key: "extra_cost_amount", label: "额外人工成本" }); + } + fields.push({ key: "remark", label: "备注", class: "document-grid-cell-wide" }); + return fields; +}); +const dispositionDocumentLineColumns = [ + { key: "index", label: "序号", width: "58px" }, + { key: "return_no", label: "退货单号", width: "18%" }, + { key: "product", label: "产品", width: "24%" }, + { key: "source_lot", label: "来源批次", width: "20%" }, + { key: "return_qty", label: "退货数量", width: "104px" }, + { key: "return_weight_kg", label: "退货重量", width: "112px" }, + { key: "responsibility_type", label: "责任归属", width: "120px" } +]; const { page: returnOrderPage, pageSize: returnOrderPageSize,