Compare commits

..

No commits in common. "a00e817065a14d5f76babe848f90f7f191c115a9" and "64e7ac346ddf903d2d56fc917b6630e10870c1a3" have entirely different histories.

11 changed files with 30 additions and 350 deletions

View File

@ -59,16 +59,6 @@ ARCHIVE_MIN_ROW_HEIGHT = 24
ARCHIVE_CELL_HORIZONTAL_PADDING = 8 ARCHIVE_CELL_HORIZONTAL_PADDING = 8
ARCHIVE_CELL_VERTICAL_PADDING = 8 ARCHIVE_CELL_VERTICAL_PADDING = 8
ARCHIVE_CELL_FONT_SIZE = 8 ARCHIVE_CELL_FONT_SIZE = 8
ARCHIVE_METADATA_FONT_SIZE = 10
ARCHIVE_METADATA_LEFT_X = 42
ARCHIVE_METADATA_RIGHT_X = 340
ARCHIVE_METADATA_COLUMN_GAP = 10
ARCHIVE_METADATA_LEFT_WIDTH = ARCHIVE_METADATA_RIGHT_X - ARCHIVE_METADATA_LEFT_X - ARCHIVE_METADATA_COLUMN_GAP
ARCHIVE_METADATA_RIGHT_WIDTH = A4[0] - ARCHIVE_TABLE_RIGHT_MARGIN - ARCHIVE_METADATA_RIGHT_X
ARCHIVE_METADATA_TOP_Y_OFFSET = 92
ARCHIVE_METADATA_MIN_ROW_HEIGHT = 20
ARCHIVE_METADATA_ROW_PADDING = 7
ARCHIVE_METADATA_TABLE_GAP = 18
ARCHIVE_STATUS_LABELS = { ARCHIVE_STATUS_LABELS = {
"ACTIVE": "启用", "ACTIVE": "启用",
@ -1034,59 +1024,6 @@ def _draw_cell(
text_y -= line_height text_y -= line_height
def _archive_metadata_rows(context: ArchiveContext) -> list[tuple[tuple[str, str], tuple[str, str]]]:
contact_text = f"{context.contact_name or ''} 电话: {context.contact_phone or ''}"
tax_text = f"{decimal_text((context.tax_rate or 0) * 100)}%"
return [
(("单据编号", context.document_no), ("单据日期", date_text(context.document_date))),
((context.partner_label, context.partner_name), (context.due_date_label, date_text(context.due_date))),
(("联系人", contact_text), ("状态", context.status)),
((context.address_label or "地址", context.address or ""), ("税率", tax_text)),
]
def _archive_metadata_line_groups(
context: ArchiveContext,
font_name: str,
size: int = ARCHIVE_METADATA_FONT_SIZE,
) -> list[tuple[list[str], list[str]]]:
groups: list[tuple[list[str], list[str]]] = []
for (left_label, left_value), (right_label, right_value) in _archive_metadata_rows(context):
left_text = f"{left_label}: {left_value or ''}"
right_text = f"{right_label}: {right_value or ''}"
groups.append(
(
_wrap_text_to_lines(left_text, font_name, size, ARCHIVE_METADATA_LEFT_WIDTH),
_wrap_text_to_lines(right_text, font_name, size, ARCHIVE_METADATA_RIGHT_WIDTH),
)
)
return groups
def _metadata_row_height(line_count: int, size: int = ARCHIVE_METADATA_FONT_SIZE) -> float:
return max(ARCHIVE_METADATA_MIN_ROW_HEIGHT, line_count * _cell_line_height(size) + ARCHIVE_METADATA_ROW_PADDING)
def _archive_table_y_for_context(context: ArchiveContext, font_name: str) -> float:
_, height = A4
y = height - ARCHIVE_METADATA_TOP_Y_OFFSET
for left_lines, right_lines in _archive_metadata_line_groups(context, font_name):
y -= _metadata_row_height(max(len(left_lines), len(right_lines), 1))
return min(height - ARCHIVE_TABLE_TOP_Y_OFFSET, y - ARCHIVE_METADATA_TABLE_GAP)
def _draw_metadata_lines(
pdf: canvas.Canvas,
x: float,
y: float,
lines: list[str],
line_height: float,
) -> None:
for line in lines:
pdf.drawString(x, y, line)
y -= line_height
def _archive_table_columns() -> list[tuple[str, int, str]]: def _archive_table_columns() -> list[tuple[str, int, str]]:
return [ return [
("", 26, "center"), ("", 26, "center"),
@ -1108,7 +1045,7 @@ def _draw_archive_page_frame(
bold_font_name: str, bold_font_name: str,
page_no: int, page_no: int,
total_pages: int, total_pages: int,
) -> float: ) -> None:
width, height = A4 width, height = A4
pdf.setFillColorRGB(0.99, 0.975, 0.92) pdf.setFillColorRGB(0.99, 0.975, 0.92)
pdf.rect(0, 0, width, height, stroke=0, fill=1) pdf.rect(0, 0, width, height, stroke=0, fill=1)
@ -1124,13 +1061,15 @@ def _draw_archive_page_frame(
pdf.drawRightString(width - 42, height - 62, "PDF归档") pdf.drawRightString(width - 42, height - 62, "PDF归档")
pdf.drawRightString(width - 42, height - 80, f"{page_no}/{total_pages}") pdf.drawRightString(width - 42, height - 80, f"{page_no}/{total_pages}")
pdf.setFont(font_name, ARCHIVE_METADATA_FONT_SIZE) pdf.setFont(font_name, 10)
line_height = _cell_line_height(ARCHIVE_METADATA_FONT_SIZE) pdf.drawString(42, height - 92, f"单据编号: {context.document_no}")
metadata_y = height - ARCHIVE_METADATA_TOP_Y_OFFSET pdf.drawString(42, height - 112, f"{context.partner_label}: {context.partner_name}")
for left_lines, right_lines in _archive_metadata_line_groups(context, font_name): pdf.drawString(42, height - 132, f"联系人: {context.contact_name or ''} 电话: {context.contact_phone or ''}")
_draw_metadata_lines(pdf, ARCHIVE_METADATA_LEFT_X, metadata_y, left_lines, line_height) pdf.drawString(42, height - 152, f"{context.address_label or '地址'}: {context.address or ''}")
_draw_metadata_lines(pdf, ARCHIVE_METADATA_RIGHT_X, metadata_y, right_lines, line_height) pdf.drawString(340, height - 92, f"单据日期: {date_text(context.document_date)}")
metadata_y -= _metadata_row_height(max(len(left_lines), len(right_lines), 1)) pdf.drawString(340, height - 112, f"{context.due_date_label}: {date_text(context.due_date)}")
pdf.drawString(340, height - 132, f"状态: {context.status}")
pdf.drawString(340, height - 152, f"税率: {decimal_text((context.tax_rate or 0) * 100)}%")
pdf.setFont(bold_font_name, 26) pdf.setFont(bold_font_name, 26)
pdf.setStrokeColorRGB(0.55, 0.05, 0.05) pdf.setStrokeColorRGB(0.55, 0.05, 0.05)
@ -1140,7 +1079,6 @@ def _draw_archive_page_frame(
pdf.rotate(-12) pdf.rotate(-12)
pdf.setStrokeColor(colors.black) pdf.setStrokeColor(colors.black)
pdf.setFillColor(colors.black) pdf.setFillColor(colors.black)
return min(height - ARCHIVE_TABLE_TOP_Y_OFFSET, metadata_y - ARCHIVE_METADATA_TABLE_GAP)
def _draw_archive_table_header(pdf: canvas.Canvas, y: float, bold_font_name: str) -> None: def _draw_archive_table_header(pdf: canvas.Canvas, y: float, bold_font_name: str) -> None:
@ -1242,13 +1180,14 @@ def render_pdf_archive(context: ArchiveContext, output_path: str | Path) -> None
output.parent.mkdir(parents=True, exist_ok=True) output.parent.mkdir(parents=True, exist_ok=True)
font_name, bold_font_name = _register_archive_font() font_name, bold_font_name = _register_archive_font()
pdf = canvas.Canvas(str(output), pagesize=A4) pdf = canvas.Canvas(str(output), pagesize=A4)
table_y = _archive_table_y_for_context(context, font_name) height = A4[1]
table_y = height - ARCHIVE_TABLE_TOP_Y_OFFSET
row_h = ARCHIVE_MIN_ROW_HEIGHT row_h = ARCHIVE_MIN_ROW_HEIGHT
max_body_height = table_y - row_h - ARCHIVE_TABLE_BOTTOM_Y max_body_height = table_y - row_h - ARCHIVE_TABLE_BOTTOM_Y
pages = _line_pages(context.lines, max_body_height, font_name) pages = _line_pages(context.lines, max_body_height, font_name)
for page_index, page_lines in enumerate(pages, start=1): for page_index, page_lines in enumerate(pages, start=1):
table_y = _draw_archive_page_frame(pdf, context, font_name, bold_font_name, page_index, len(pages)) _draw_archive_page_frame(pdf, context, font_name, bold_font_name, page_index, len(pages))
_draw_archive_table_header(pdf, table_y, bold_font_name) _draw_archive_table_header(pdf, table_y, bold_font_name)
y = _draw_archive_table_rows( y = _draw_archive_table_rows(
pdf, pdf,

View File

@ -43,7 +43,6 @@ from app.services.document_archives import ( # noqa: E402
get_latest_archive, get_latest_archive,
render_pdf_archive, render_pdf_archive,
_archive_table_columns, _archive_table_columns,
_draw_archive_page_frame,
_draw_cell, _draw_cell,
_draw_archive_footer, _draw_archive_footer,
) )
@ -733,77 +732,6 @@ class DocumentArchiveServiceTest(unittest.TestCase):
rendered_width = pdfmetrics.stringWidth(line, fake_pdf.font_name, fake_pdf.size) rendered_width = pdfmetrics.stringWidth(line, fake_pdf.font_name, fake_pdf.size)
self.assertLessEqual(rendered_width, 54) self.assertLessEqual(rendered_width, 54)
def test_draw_archive_page_frame_wraps_long_metadata_inside_columns(self) -> None:
class FakePdf:
def __init__(self) -> None:
self.font_name = "Helvetica"
self.size = 10
self.drawn_texts: list[tuple[float, float, str, str, int]] = []
def setFillColorRGB(self, *args, **kwargs) -> None:
_ = args, kwargs
def rect(self, *args, **kwargs) -> None:
_ = args, kwargs
def setStrokeColor(self, *args, **kwargs) -> None:
_ = args, kwargs
def setStrokeColorRGB(self, *args, **kwargs) -> None:
_ = args, kwargs
def setFillColor(self, *args, **kwargs) -> None:
_ = args, kwargs
def setFont(self, font_name, size) -> None:
self.font_name = font_name
self.size = size
def drawRightString(self, x, y, text) -> None:
_ = x, y, text
def drawCentredString(self, x, y, text) -> None:
_ = x, y, text
def drawString(self, x, y, text) -> None:
self.drawn_texts.append((x, y, text, self.font_name, self.size))
def rotate(self, *args, **kwargs) -> None:
_ = args, kwargs
fake_pdf = FakePdf()
context = ArchiveContext(
document_type=DOCUMENT_TYPE_SALES_ORDER,
business_id=100,
document_no="生产领料-JH_不锈钢板-用于测试00101_0001-苹果电脑后盖-超长批次-客户特殊版本-返工追溯链路",
title="生产领料出库单归档",
partner_label="生产产品",
partner_name="苹果电脑后盖-测试",
document_date=date(2026, 6, 11),
due_date_label="生产台账",
due_date=2,
status="在生产",
tax_rate=0,
total_amount=636,
contact_name="",
contact_phone="",
address="原材料库",
)
table_y = _draw_archive_page_frame(fake_pdf, context, "Helvetica", "Helvetica-Bold", 1, 1)
metadata_lines = [
(text, font_name, size)
for x, y, text, font_name, size in fake_pdf.drawn_texts
if x == 42 and y > A4[1] - 190
]
document_no_lines = [text for text, _, _ in metadata_lines if "单据编号" in text or "返工追溯" in text]
self.assertGreaterEqual(len(document_no_lines), 2)
self.assertLess(table_y, A4[1] - 190)
for text, font_name, size in metadata_lines:
rendered_width = pdfmetrics.stringWidth(text, font_name, size)
self.assertLessEqual(rendered_width, 288)
def test_generate_archive_failed_row_uses_free_version_after_collision(self) -> None: def test_generate_archive_failed_row_uses_free_version_after_collision(self) -> None:
order = self._seed_sales_order() order = self._seed_sales_order()

View File

@ -5,7 +5,6 @@ const files = [
"src/components/documentForms/DocumentPaper.vue", "src/components/documentForms/DocumentPaper.vue",
"src/components/documentForms/DocumentGrid.vue", "src/components/documentForms/DocumentGrid.vue",
"src/components/documentForms/DocumentLineTable.vue", "src/components/documentForms/DocumentLineTable.vue",
"src/components/documentForms/WarehouseDocumentFormShell.vue",
"src/components/documentForms/DocumentArchiveActions.vue", "src/components/documentForms/DocumentArchiveActions.vue",
"src/styles/main.css" "src/styles/main.css"
]; ];
@ -40,17 +39,6 @@ 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"]) { for (const forbiddenSnippet of ["ghost-button", "action-button"]) {
if (archiveActions.includes(forbiddenSnippet)) { if (archiveActions.includes(forbiddenSnippet)) {
throw new Error(`DocumentArchiveActions must not use global button class: ${forbiddenSnippet}`); throw new Error(`DocumentArchiveActions must not use global button class: ${forbiddenSnippet}`);
@ -70,25 +58,6 @@ 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 [ for (const selector of [
".document-archive-button", ".document-archive-button",
".document-archive-button-preview", ".document-archive-button-preview",

View File

@ -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.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, /后端自动生成库存批次号|系统自动生成库存批次号/, "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, /uploadResource/, "purchase receipt UI should upload logistics auxiliary photos");
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, /selectedPurchaseTargetType\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, /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, /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"); 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");

View File

@ -10,7 +10,7 @@
<span>仓库联单</span> <span>仓库联单</span>
<strong>{{ companyName }}</strong> <strong>{{ companyName }}</strong>
<span>业务日期</span> <span>业务日期</span>
<strong>{{ displayBusinessDate }}</strong> <strong>{{ businessDate || "-" }}</strong>
</div> </div>
<main class="warehouse-document-body"> <main class="warehouse-document-body">
@ -21,12 +21,9 @@
</template> </template>
<script setup> <script setup>
import { computed } from "vue";
import DocumentPaper from "./DocumentPaper.vue"; import DocumentPaper from "./DocumentPaper.vue";
import { formatChineseDate } from "../../utils/formatters";
const props = defineProps({ defineProps({
title: { title: {
type: String, type: String,
required: true required: true
@ -36,7 +33,7 @@ const props = defineProps({
default: "" default: ""
}, },
businessDate: { businessDate: {
type: [String, Date], type: String,
default: "" default: ""
}, },
tone: { tone: {
@ -50,5 +47,4 @@ const props = defineProps({
}); });
const emit = defineEmits(["submit"]); const emit = defineEmits(["submit"]);
const displayBusinessDate = computed(() => formatChineseDate(props.businessDate));
</script> </script>

View File

@ -16005,34 +16005,6 @@ body .login-production-layout .login-security-foot p {
word-break: break-word; 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 { .document-line-table {
margin-top: 18px; margin-top: 18px;
border: 2px solid var(--document-paper-grid); border: 2px solid var(--document-paper-grid);

View File

@ -2,35 +2,14 @@ export function formatDate(value) {
if (!value) { if (!value) {
return "-"; return "-";
} }
const parts = resolveDateParts(value); return String(value).slice(0, 10);
if (!parts) {
return "-";
}
return `${parts.year}-${parts.month}-${parts.day}`;
} }
export function formatDateTime(value) { export function formatDateTime(value) {
if (!value) { if (!value) {
return "-"; return "-";
} }
const parts = resolveDateParts(value); return String(value).replace("T", " ").slice(0, 16);
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) { export function formatAmount(value) {
@ -54,64 +33,3 @@ export function formatWeight(value) {
export function emptyToNull(value) { export function emptyToNull(value) {
return value === "" || value === undefined ? null : 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");
}

View File

@ -1,34 +0,0 @@
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(""), "-");
});
});

View File

@ -65,11 +65,6 @@ describe("InventoryLedgerView warehouse action layout", () => {
assert.match(source, /<WarehouseDocumentActionBar/); assert.match(source, /<WarehouseDocumentActionBar/);
}); });
it("passes native dates to the paper document shell so it can render Chinese business dates", () => {
assert.doesNotMatch(source, /formatDateTime\(new Date\(\)\)/);
assert.match(source, /:business-date="new Date\(\)"/);
});
it("surfaces generated PDF archive feedback after independent warehouse saves", () => { it("surfaces generated PDF archive feedback after independent warehouse saves", () => {
[ [
["submitRawReturnOutbound", "退货出库"], ["submitRawReturnOutbound", "退货出库"],

View File

@ -329,7 +329,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="客料入库单" title="客料入库单"
:document-no="customerSuppliedLotNoPreview || '提交后生成'" :document-no="customerSuppliedLotNoPreview || '提交后生成'"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
@submit="submitGenericOperation" @submit="submitGenericOperation"
> >
<div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div> <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
@ -447,7 +447,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
:title="genericOperationDocumentTitle" :title="genericOperationDocumentTitle"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
@submit="submitGenericOperation" @submit="submitGenericOperation"
> >
<div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div> <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
@ -806,7 +806,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
:title="`${activeInventoryLabel}${specialAdjustmentDirectionText}单`" :title="`${activeInventoryLabel}${specialAdjustmentDirectionText}单`"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
:tone="specialAdjustmentForm.direction === 'OUT' ? 'red' : 'green'" :tone="specialAdjustmentForm.direction === 'OUT' ? 'red' : 'green'"
@submit="submitSpecialAdjustment" @submit="submitSpecialAdjustment"
> >
@ -996,7 +996,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="原材料退货出库单" title="原材料退货出库单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
tone="red" tone="red"
@submit="submitRawReturnOutbound" @submit="submitRawReturnOutbound"
> >
@ -1114,7 +1114,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="退货入库单" title="退货入库单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
@submit="submitReturnInbound" @submit="submitReturnInbound"
> >
<div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div> <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
@ -1212,7 +1212,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="退货废料入库单" title="退货废料入库单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
@submit="submitReturnScrapInbound" @submit="submitReturnScrapInbound"
> >
<div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div> <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
@ -1350,7 +1350,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="生产出库单" title="生产出库单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
tone="red" tone="red"
novalidate novalidate
@submit="submitProductionOut" @submit="submitProductionOut"
@ -1461,7 +1461,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
:title="isReworkCompletionOperation ? '返工入库单' : '生产入库单'" :title="isReworkCompletionOperation ? '返工入库单' : '生产入库单'"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
@submit="submitCompletionInbound" @submit="submitCompletionInbound"
> >
<div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div> <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>
@ -1601,7 +1601,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="销售出库单" title="销售出库单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
tone="red" tone="red"
@submit="submitSalesOut" @submit="submitSalesOut"
> >
@ -1817,7 +1817,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="返工出库单" title="返工出库单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
tone="red" tone="red"
@submit="submitReturnReworkOutbound" @submit="submitReturnReworkOutbound"
> >
@ -2034,7 +2034,7 @@
class="warehouse-document-operation-form" class="warehouse-document-operation-form"
title="生产台账入库结算单" title="生产台账入库结算单"
document-no="保存后生成" document-no="保存后生成"
:business-date="new Date()" :business-date="formatDateTime(new Date())"
@submit="submitProductionWorkOrderInbound" @submit="submitProductionWorkOrderInbound"
> >
<div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div> <div v-if="feedbackMessage" class="feedback-banner">{{ feedbackMessage }}</div>

View File

@ -857,13 +857,10 @@ onMounted(async () => {
.receipt-document-line-static { .receipt-document-line-static {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
min-width: 0;
max-width: 100%;
min-height: 34px; min-height: 34px;
color: var(--document-paper-ink, #17120a); color: var(--document-paper-ink, #17120a);
font-weight: 800; font-weight: 800;
line-height: 1.45; line-height: 1.45;
overflow-wrap: anywhere;
} }
.receipt-document-line-static { .receipt-document-line-static {