diff --git a/frontend/scripts/test-document-form-components.mjs b/frontend/scripts/test-document-form-components.mjs
index e772444..01b9d60 100644
--- a/frontend/scripts/test-document-form-components.mjs
+++ b/frontend/scripts/test-document-form-components.mjs
@@ -5,6 +5,7 @@ const files = [
"src/components/documentForms/DocumentPaper.vue",
"src/components/documentForms/DocumentGrid.vue",
"src/components/documentForms/DocumentLineTable.vue",
+ "src/components/documentForms/WarehouseDocumentFormShell.vue",
"src/components/documentForms/DocumentArchiveActions.vue",
"src/styles/main.css"
];
@@ -39,6 +40,17 @@ for (const snippet of ["预览PDF", "下载PDF", "重新生成"]) {
}
}
+const warehouseDocumentFormShell = sources.get("src/components/documentForms/WarehouseDocumentFormShell.vue");
+for (const snippet of ["formatChineseDate", "displayBusinessDate"]) {
+ if (!warehouseDocumentFormShell.includes(snippet)) {
+ throw new Error(`WarehouseDocumentFormShell missing business date formatting snippet: ${snippet}`);
+ }
+}
+
+if (warehouseDocumentFormShell.includes("{{ businessDate || \"-\" }}")) {
+ throw new Error("WarehouseDocumentFormShell must not render raw businessDate values");
+}
+
for (const forbiddenSnippet of ["ghost-button", "action-button"]) {
if (archiveActions.includes(forbiddenSnippet)) {
throw new Error(`DocumentArchiveActions must not use global button class: ${forbiddenSnippet}`);
@@ -58,6 +70,25 @@ for (const selector of [".document-paper-shell", ".document-paper-sheet", ".docu
}
}
+for (const selector of [
+ ".document-grid-value input",
+ ".document-grid-value select",
+ ".document-grid-value textarea",
+ ".document-line-table input",
+ ".document-line-table select",
+ ".document-line-table textarea"
+]) {
+ if (!css.includes(selector)) {
+ throw new Error(`main.css missing document form overflow guard selector: ${selector}`);
+ }
+}
+
+for (const snippet of ["box-sizing: border-box", "min-width: 0", "max-width: 100%", "text-overflow: ellipsis"]) {
+ if (!css.includes(snippet)) {
+ throw new Error(`document form controls missing overflow guard declaration: ${snippet}`);
+ }
+}
+
for (const selector of [
".document-archive-button",
".document-archive-button-preview",
diff --git a/frontend/scripts/test-purchase-receipt-inventory-lot-ui.mjs b/frontend/scripts/test-purchase-receipt-inventory-lot-ui.mjs
index dab88cc..39bf2e5 100644
--- a/frontend/scripts/test-purchase-receipt-inventory-lot-ui.mjs
+++ b/frontend/scripts/test-purchase-receipt-inventory-lot-ui.mjs
@@ -10,7 +10,7 @@ assert.doesNotMatch(source, /YL-NB|buildDefaultLotNo/, "purchase receipt UI must
assert.doesNotMatch(source, /送货单号|supplier_delivery_no/, "purchase receipt UI should not keep the duplicate supplier delivery number field");
assert.match(source, /后端自动生成库存批次号|系统自动生成库存批次号/, "purchase receipt UI should explain that inventory lot numbers are generated by the backend");
assert.match(source, /uploadResource/, "purchase receipt UI should upload logistics auxiliary photos");
-assert.match(source, /selectedPurchaseTargetType\s*===\s*["']RAW["'][\s\S]*运单号[\s\S]*辅助照片/, "raw purchase receipt UI should show waybill and auxiliary photo fields");
+assert.match(source, /selectedPurchaseTargetType(?:\.value)?\s*===\s*["']RAW["'][\s\S]*运单号[\s\S]*辅助照片/, "raw purchase receipt UI should show waybill and auxiliary photo fields");
assert.match(source, /validateLogisticsForm\s*\(\s*\)/, "raw purchase receipt submit should validate logistics information before posting");
assert.match(source, /waybill_no:\s*selectedPurchaseTargetType\.value\s*===\s*["']RAW["']\s*\?\s*form\.waybill_no/, "purchase receipt payload should include raw waybill number");
assert.match(source, /order_photo_url:\s*selectedPurchaseTargetType\.value\s*===\s*["']RAW["']\s*\?\s*form\.order_photo_url/, "purchase receipt payload should include raw auxiliary photo URL");
diff --git a/frontend/src/components/documentForms/WarehouseDocumentFormShell.vue b/frontend/src/components/documentForms/WarehouseDocumentFormShell.vue
index 6988450..d91c1c4 100644
--- a/frontend/src/components/documentForms/WarehouseDocumentFormShell.vue
+++ b/frontend/src/components/documentForms/WarehouseDocumentFormShell.vue
@@ -10,7 +10,7 @@
仓库联单
{{ companyName }}
业务日期
- {{ businessDate || "-" }}
+ {{ displayBusinessDate }}
@@ -21,9 +21,12 @@
diff --git a/frontend/src/styles/main.css b/frontend/src/styles/main.css
index 616ea50..ec4705b 100644
--- a/frontend/src/styles/main.css
+++ b/frontend/src/styles/main.css
@@ -16005,6 +16005,34 @@ body .login-production-layout .login-security-foot p {
word-break: break-word;
}
+.document-grid-value input,
+.document-grid-value select,
+.document-grid-value textarea,
+.document-line-table input,
+.document-line-table select,
+.document-line-table textarea {
+ display: block;
+ width: 100%;
+ min-width: 0;
+ max-width: 100%;
+ box-sizing: border-box;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.document-grid-value input,
+.document-grid-value select,
+.document-line-table input,
+.document-line-table select {
+ white-space: nowrap;
+}
+
+.document-grid-value textarea,
+.document-line-table textarea {
+ white-space: normal;
+ overflow-wrap: anywhere;
+}
+
.document-line-table {
margin-top: 18px;
border: 2px solid var(--document-paper-grid);
diff --git a/frontend/src/utils/formatters.js b/frontend/src/utils/formatters.js
index 6123df0..18c9e2b 100644
--- a/frontend/src/utils/formatters.js
+++ b/frontend/src/utils/formatters.js
@@ -2,14 +2,35 @@ export function formatDate(value) {
if (!value) {
return "-";
}
- return String(value).slice(0, 10);
+ const parts = resolveDateParts(value);
+ if (!parts) {
+ return "-";
+ }
+ return `${parts.year}-${parts.month}-${parts.day}`;
}
export function formatDateTime(value) {
if (!value) {
return "-";
}
- return String(value).replace("T", " ").slice(0, 16);
+ const parts = resolveDateParts(value);
+ if (!parts) {
+ return "-";
+ }
+ const time = resolveTimeParts(value);
+ const dateText = `${parts.year}-${parts.month}-${parts.day}`;
+ return time ? `${dateText} ${time.hour}:${time.minute}` : dateText;
+}
+
+export function formatChineseDate(value) {
+ if (!value) {
+ return "-";
+ }
+ const parts = resolveDateParts(value);
+ if (!parts) {
+ return "-";
+ }
+ return `${parts.year}年${parts.month}月${parts.day}日`;
}
export function formatAmount(value) {
@@ -33,3 +54,64 @@ export function formatWeight(value) {
export function emptyToNull(value) {
return value === "" || value === undefined ? null : value;
}
+
+function resolveDateParts(value) {
+ if (value instanceof Date) {
+ return datePartsFromDate(value);
+ }
+
+ const stringValue = String(value).trim();
+ const dateMatch = stringValue.match(/^(\d{4})[-/年](\d{1,2})[-/月](\d{1,2})/);
+ if (dateMatch) {
+ return {
+ year: dateMatch[1],
+ month: padDatePart(dateMatch[2]),
+ day: padDatePart(dateMatch[3])
+ };
+ }
+
+ const parsedDate = new Date(stringValue);
+ return datePartsFromDate(parsedDate);
+}
+
+function resolveTimeParts(value) {
+ if (value instanceof Date) {
+ return timePartsFromDate(value);
+ }
+
+ const stringValue = String(value).trim();
+ const timeMatch = stringValue.match(/(?:T|\s)(\d{1,2}):(\d{1,2})/);
+ if (timeMatch) {
+ return {
+ hour: padDatePart(timeMatch[1]),
+ minute: padDatePart(timeMatch[2])
+ };
+ }
+
+ return null;
+}
+
+function datePartsFromDate(date) {
+ if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
+ return null;
+ }
+ return {
+ year: String(date.getFullYear()),
+ month: padDatePart(date.getMonth() + 1),
+ day: padDatePart(date.getDate())
+ };
+}
+
+function timePartsFromDate(date) {
+ if (!(date instanceof Date) || Number.isNaN(date.getTime())) {
+ return null;
+ }
+ return {
+ hour: padDatePart(date.getHours()),
+ minute: padDatePart(date.getMinutes())
+ };
+}
+
+function padDatePart(value) {
+ return String(value).padStart(2, "0");
+}
diff --git a/frontend/src/utils/formatters.test.js b/frontend/src/utils/formatters.test.js
new file mode 100644
index 0000000..bef1dd5
--- /dev/null
+++ b/frontend/src/utils/formatters.test.js
@@ -0,0 +1,34 @@
+import assert from "node:assert/strict";
+import { describe, it } from "node:test";
+
+import { formatChineseDate, formatDate, formatDateTime } from "./formatters.js";
+
+describe("formatDate", () => {
+ it("formats Date values without browser-native English text", () => {
+ assert.equal(formatDate(new Date(2026, 5, 13, 9, 30)), "2026-06-13");
+ });
+});
+
+describe("formatDateTime", () => {
+ it("formats Date values as numeric date-time text", () => {
+ assert.equal(formatDateTime(new Date(2026, 5, 13, 9, 30)), "2026-06-13 09:30");
+ });
+
+ it("keeps date-only strings as date text instead of adding synthetic time", () => {
+ assert.equal(formatDateTime("2026-06-13"), "2026-06-13");
+ });
+});
+
+describe("formatChineseDate", () => {
+ it("formats Date values as Chinese year-month-day text", () => {
+ assert.equal(formatChineseDate(new Date(2026, 5, 13, 9, 30)), "2026年06月13日");
+ });
+
+ it("formats ISO date strings as Chinese year-month-day text", () => {
+ assert.equal(formatChineseDate("2026-06-13T15:45:00"), "2026年06月13日");
+ });
+
+ it("keeps empty values aligned with other display formatters", () => {
+ assert.equal(formatChineseDate(""), "-");
+ });
+});
diff --git a/frontend/src/views/InventoryLedgerView.test.js b/frontend/src/views/InventoryLedgerView.test.js
index 1b8f433..fa81e48 100644
--- a/frontend/src/views/InventoryLedgerView.test.js
+++ b/frontend/src/views/InventoryLedgerView.test.js
@@ -65,6 +65,11 @@ describe("InventoryLedgerView warehouse action layout", () => {
assert.match(source, / {
+ assert.doesNotMatch(source, /formatDateTime\(new Date\(\)\)/);
+ assert.match(source, /:business-date="new Date\(\)"/);
+ });
+
it("surfaces generated PDF archive feedback after independent warehouse saves", () => {
[
["submitRawReturnOutbound", "退货出库"],
diff --git a/frontend/src/views/InventoryLedgerView.vue b/frontend/src/views/InventoryLedgerView.vue
index a9d0e74..b096aa5 100644
--- a/frontend/src/views/InventoryLedgerView.vue
+++ b/frontend/src/views/InventoryLedgerView.vue
@@ -329,7 +329,7 @@
class="warehouse-document-operation-form"
title="客料入库单"
:document-no="customerSuppliedLotNoPreview || '提交后生成'"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
@submit="submitGenericOperation"
>
{{ feedbackMessage }}
@@ -447,7 +447,7 @@
class="warehouse-document-operation-form"
:title="genericOperationDocumentTitle"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
@submit="submitGenericOperation"
>
{{ feedbackMessage }}
@@ -806,7 +806,7 @@
class="warehouse-document-operation-form"
:title="`${activeInventoryLabel}${specialAdjustmentDirectionText}单`"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
:tone="specialAdjustmentForm.direction === 'OUT' ? 'red' : 'green'"
@submit="submitSpecialAdjustment"
>
@@ -996,7 +996,7 @@
class="warehouse-document-operation-form"
title="原材料退货出库单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
tone="red"
@submit="submitRawReturnOutbound"
>
@@ -1114,7 +1114,7 @@
class="warehouse-document-operation-form"
title="退货入库单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
@submit="submitReturnInbound"
>
{{ feedbackMessage }}
@@ -1212,7 +1212,7 @@
class="warehouse-document-operation-form"
title="退货废料入库单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
@submit="submitReturnScrapInbound"
>
{{ feedbackMessage }}
@@ -1350,7 +1350,7 @@
class="warehouse-document-operation-form"
title="生产出库单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
tone="red"
novalidate
@submit="submitProductionOut"
@@ -1461,7 +1461,7 @@
class="warehouse-document-operation-form"
:title="isReworkCompletionOperation ? '返工入库单' : '生产入库单'"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
@submit="submitCompletionInbound"
>
{{ feedbackMessage }}
@@ -1601,7 +1601,7 @@
class="warehouse-document-operation-form"
title="销售出库单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
tone="red"
@submit="submitSalesOut"
>
@@ -1817,7 +1817,7 @@
class="warehouse-document-operation-form"
title="返工出库单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
tone="red"
@submit="submitReturnReworkOutbound"
>
@@ -2034,7 +2034,7 @@
class="warehouse-document-operation-form"
title="生产台账入库结算单"
document-no="保存后生成"
- :business-date="formatDateTime(new Date())"
+ :business-date="new Date()"
@submit="submitProductionWorkOrderInbound"
>
{{ feedbackMessage }}
diff --git a/frontend/src/views/PurchaseReceiptView.vue b/frontend/src/views/PurchaseReceiptView.vue
index 47cc1f9..2446a0c 100644
--- a/frontend/src/views/PurchaseReceiptView.vue
+++ b/frontend/src/views/PurchaseReceiptView.vue
@@ -857,10 +857,13 @@ onMounted(async () => {
.receipt-document-line-static {
display: inline-flex;
align-items: center;
+ min-width: 0;
+ max-width: 100%;
min-height: 34px;
color: var(--document-paper-ink, #17120a);
font-weight: 800;
line-height: 1.45;
+ overflow-wrap: anywhere;
}
.receipt-document-line-static {