const PRINT_TITLE = "销售发货单";
|
|
const escapeHtml = (value) =>
|
String(value ?? "")
|
.replaceAll("&", "&")
|
.replaceAll("<", "<")
|
.replaceAll(">", ">")
|
.replaceAll('"', """)
|
.replaceAll("'", "'");
|
|
const toNumber = (value) => {
|
const num = Number(value);
|
return Number.isFinite(num) ? num : 0;
|
};
|
|
const formatDisplayDate = (value) => {
|
if (!value) return "";
|
const date = new Date(value);
|
if (Number.isNaN(date.getTime())) return String(value);
|
const year = date.getFullYear();
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
const day = String(date.getDate()).padStart(2, "0");
|
return `${year}/${month}/${day}`;
|
};
|
|
const getItemArea = (item) => toNumber(item?.area || item?.settleTotalArea || item?.actualTotalArea);
|
|
const getOrderNo = (data, row, item) =>
|
item?.salesContractNo || item?.orderNo || data?.salesContractNo || row?.salesContractNo || "";
|
|
const splitItemsByPage = (items, pageSize) => {
|
const list = Array.isArray(items) ? items : [];
|
if (list.length === 0) return [[]];
|
const pages = [];
|
for (let i = 0; i < list.length; i += pageSize) {
|
pages.push(list.slice(i, i + pageSize));
|
}
|
return pages;
|
};
|
|
const normalizeInvoiceData = (raw, selectedRow) => {
|
const data = raw ?? {};
|
const groups = Array.isArray(data.groups) ? data.groups : [];
|
if (!groups.length) return data;
|
|
const items = groups.flatMap((group) =>
|
(Array.isArray(group?.items) ? group.items : []).map((item) => ({
|
...item,
|
productDescription: group?.productName || item?.productDescription || "",
|
salesContractNo: group?.salesContractNo || item?.salesContractNo || "",
|
widthHeight: item?.widthHeight || "",
|
}))
|
);
|
|
return {
|
...data,
|
items,
|
customerName: data.customerName || selectedRow?.customerName || "",
|
contactPerson: data.contactPerson || selectedRow?.contactPerson || "",
|
contactPhone: data.contactPhone || selectedRow?.contactPhone || "",
|
deliveryAddress:
|
data.companyAddress || data.deliveryAddress || data.shippingAddress || selectedRow?.deliveryAddress || "",
|
shipmentNo: data.externalOrderNo || data.shipmentNo || "",
|
register: data.orderMaker || data.register || selectedRow?.entryPersonName || "",
|
registerDate: data.executionDate || data.registerDate || data.entryDate || selectedRow?.entryDate || "",
|
};
|
};
|
|
const groupByProduct = (items, data, row) => {
|
const list = Array.isArray(items) ? items : [];
|
const map = new Map();
|
list.forEach((item) => {
|
const key = `${item?.productDescription || ""}__${getOrderNo(data, row, item)}`;
|
if (!map.has(key)) {
|
map.set(key, {
|
productName: item?.productDescription || "",
|
orderNo: getOrderNo(data, row, item),
|
items: [],
|
});
|
}
|
map.get(key).items.push(item);
|
});
|
return Array.from(map.values());
|
};
|
|
const renderItemRows = (items, startIndex) =>
|
items
|
.map((item, idx) => {
|
const sizeText = item?.widthHeight
|
? escapeHtml(item.widthHeight)
|
: item?.width || item?.height
|
? `${escapeHtml(item?.width)} * ${escapeHtml(item?.height)}`
|
: "";
|
return `
|
<tr>
|
<td>${startIndex + idx + 1}</td>
|
<td class="left">${escapeHtml(item?.floorCode)}</td>
|
<td>${sizeText}</td>
|
<td>${toNumber(item?.quantity) || ""}</td>
|
<td>${getItemArea(item) ? getItemArea(item).toFixed(2) : ""}</td>
|
<td class="left">${escapeHtml(item?.remark)}</td>
|
<td class="left">${escapeHtml(item?.processRequirement)}</td>
|
</tr>
|
`;
|
})
|
.join("");
|
|
export const printSalesDeliveryNote = (rawData, selectedRow = {}) => {
|
const data = normalizeInvoiceData(rawData, selectedRow);
|
const allItems = Array.isArray(data.items) ? data.items : [];
|
const pageSize = 18;
|
const itemPages = splitItemsByPage(allItems, pageSize);
|
const totalPages = itemPages.length;
|
|
const printWindow = window.open("", "_blank", "width=1200,height=900");
|
if (!printWindow) {
|
throw new Error("浏览器拦截了弹窗,请允许弹窗后重试");
|
}
|
|
const html = `
|
<!DOCTYPE html>
|
<html>
|
<head>
|
<meta charset="UTF-8" />
|
<title>${PRINT_TITLE}</title>
|
<style>
|
body { margin: 0; padding: 0; font-family: "SimSun", serif; color: #222; }
|
.page { width: 198mm; margin: 0 auto; padding: 4mm 4mm 6mm; box-sizing: border-box; page-break-after: always; }
|
.page:last-child { page-break-after: auto; }
|
.head-top {
|
display: grid;
|
grid-template-columns: 1fr auto 1fr;
|
align-items: end;
|
margin-bottom: 1px;
|
}
|
.factory {
|
grid-column: 2;
|
text-align: center;
|
font-size: 20px;
|
font-weight: 700;
|
line-height: 1.2;
|
}
|
.page-mark {
|
grid-column: 3;
|
justify-self: end;
|
font-size: 12px;
|
margin-right: 8mm;
|
margin-bottom: 1px;
|
}
|
.head-mid {
|
display: grid;
|
grid-template-columns: 1fr auto 1fr;
|
align-items: center;
|
margin-bottom: 2px;
|
}
|
.head-mid-left { font-size: 13px; text-align: left; }
|
.head-mid-title { font-size: 20px; font-weight: 700; text-align: center; }
|
.head-mid-right { font-size: 13px; text-align: right; padding-right: 8mm; }
|
table { width: 100%; border-collapse: collapse; table-layout: fixed; border: 1px solid #222; }
|
td, th { border: 1px solid #222; padding: 2px 4px; font-size: 13px; text-align: center; vertical-align: middle; }
|
.left { text-align: left; }
|
.group-title td { font-weight: 700; }
|
.subtotal td, .total-row td { font-weight: 700; }
|
.empty td { height: 120px; color: #666; }
|
.footer { margin-top: 6px; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 6px; font-size: 13px; }
|
@media print {
|
@page { size: A4 portrait; margin: 8mm; }
|
.page { width: 100%; margin: 0; padding: 0; }
|
}
|
</style>
|
</head>
|
<body>
|
${itemPages
|
.map((pageItems, pageIndex) => {
|
const pageGroups = groupByProduct(pageItems, data, selectedRow);
|
let serial = pageIndex * pageSize;
|
const totalQty = toNumber(data.totalQuantity) || allItems.reduce((s, it) => s + toNumber(it?.quantity), 0);
|
const totalArea = toNumber(data.totalArea) || allItems.reduce((s, it) => s + getItemArea(it), 0);
|
return `
|
<div class="page">
|
<div class="head-top">
|
<div></div>
|
<div class="factory">鹤壁天沐钢化玻璃厂</div>
|
<div class="page-mark">第${pageIndex + 1}页,共${totalPages}页</div>
|
</div>
|
<div class="head-mid">
|
<div class="head-mid-left">对方单号: ${escapeHtml(data.deliveryNo || data.shippingNo || selectedRow.expressNumber || "")}</div>
|
<div class="head-mid-title">销售发货单</div>
|
<div class="head-mid-right">发货单号: ${escapeHtml(data.shipmentNo || data.deliveryNo || "")}</div>
|
</div>
|
<table>
|
<tr>
|
<td class="left" colspan="4">客户名称: ${escapeHtml(data.customerName || selectedRow.customerName || "")}</td>
|
<td class="left" colspan="3">联系人: ${escapeHtml(data.contactPerson || selectedRow.contactPerson || "")}</td>
|
</tr>
|
<tr>
|
<td class="left" colspan="4">发货地址: ${escapeHtml(data.deliveryAddress || data.shippingAddress || selectedRow.deliveryAddress || "")}</td>
|
<td class="left" colspan="3">联系电话: ${escapeHtml(data.contactPhone || selectedRow.contactPhone || "")}</td>
|
</tr>
|
<tr>
|
<th style="width:8%;">序号</th>
|
<th style="width:22%;">楼层编号</th>
|
<th style="width:20%;">宽(弧长)*高</th>
|
<th style="width:10%;">数量</th>
|
<th style="width:12%;">面积</th>
|
<th style="width:10%;">备注</th>
|
<th style="width:18%;">加工要求</th>
|
</tr>
|
${
|
pageGroups.length
|
? pageGroups
|
.map((group) => {
|
const subQty = group.items.reduce((s, it) => s + toNumber(it?.quantity), 0);
|
const subArea = group.items.reduce((s, it) => s + getItemArea(it), 0);
|
const rows = renderItemRows(group.items, serial);
|
serial += group.items.length;
|
return `
|
<tr class="group-title">
|
<td colspan="5" class="left">产品名称: ${escapeHtml(group.productName)}</td>
|
<td colspan="2" class="left">订单编号: ${escapeHtml(group.orderNo)}</td>
|
</tr>
|
${rows}
|
<tr class="subtotal">
|
<td colspan="3">小计:</td>
|
<td>${subQty || ""}</td>
|
<td>${subArea ? subArea.toFixed(2) : ""}</td>
|
<td colspan="2"></td>
|
</tr>
|
`;
|
})
|
.join("")
|
: `<tr class="empty"><td colspan="7">暂无明细</td></tr>`
|
}
|
${
|
pageIndex === totalPages - 1
|
? `
|
<tr class="total-row">
|
<td colspan="3">合计:</td>
|
<td>${totalQty || ""}</td>
|
<td>${totalArea ? totalArea.toFixed(2) : ""}</td>
|
<td colspan="2"></td>
|
</tr>
|
`
|
: ""
|
}
|
</table>
|
${
|
pageIndex === totalPages - 1
|
? `
|
<div class="footer">
|
<div>制 单 员: ${escapeHtml(data.register || selectedRow.entryPersonName || "")}</div>
|
<div>制单日期: ${escapeHtml(formatDisplayDate(data.registerDate || data.entryDate || selectedRow.entryDate))}</div>
|
<div>客户签字:</div>
|
<div>签收日期:</div>
|
</div>
|
`
|
: ""
|
}
|
</div>
|
`;
|
})
|
.join("")}
|
</body>
|
</html>
|
`;
|
|
printWindow.document.write(html);
|
printWindow.document.close();
|
printWindow.onload = () => {
|
setTimeout(() => {
|
printWindow.focus();
|
printWindow.print();
|
printWindow.close();
|
}, 300);
|
};
|
};
|