import QRCode from "qrcode"; 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 PRODUCT_NAME_FIELD_KEYS = [ "productDescription", "productName", "name", "title", "goodsName", "materialName", "glassName", ]; const PRODUCT_NAME_LIST_FIELD_KEYS = [ "productDescriptionList", "productNameList", "productDescriptions", "productNames", "nameList", "goodsNameList", "detailProductNames", "productInfoList", ]; const SPECIFICATION_FIELD_KEYS = ["specificationModel", "specification", "model", "spec"]; const normalizeNameList = (value) => { if (Array.isArray(value)) { return value .flatMap((item) => normalizeNameList(item)) .map((item) => String(item).trim()) .filter(Boolean); } if (value && typeof value === "object") { const objectListNames = PRODUCT_NAME_LIST_FIELD_KEYS.flatMap((key) => normalizeNameList(value?.[key])); if (objectListNames.length) return objectListNames; return PRODUCT_NAME_FIELD_KEYS.flatMap((key) => normalizeNameList(value?.[key])); } if (typeof value === "string") { const text = value.trim(); if (!text) return []; const parts = text .split(/[,\n,、;;]/) .map((item) => item.trim()) .filter(Boolean); return parts.length > 1 ? parts : [text]; } if (value === null || value === undefined) return []; const text = String(value).trim(); return text ? [text] : []; }; const extractNameListByKeys = (source, keys) => { if (!source || typeof source !== "object") return []; return keys.flatMap((key) => normalizeNameList(source?.[key])); }; const resolveProductName = (item, fallbackNames, index) => { const itemNames = [ ...extractNameListByKeys(item, PRODUCT_NAME_LIST_FIELD_KEYS), ...extractNameListByKeys(item, PRODUCT_NAME_FIELD_KEYS), ]; if (itemNames.length > 1) { return itemNames[index] || itemNames[0] || ""; } return itemNames[0] || fallbackNames[index] || fallbackNames[0] || ""; }; const resolveSpecificationModel = (...sources) => { for (const source of sources) { if (!source || typeof source !== "object") continue; for (const key of SPECIFICATION_FIELD_KEYS) { const value = source?.[key]; if (value !== undefined && value !== null && String(value).trim()) { return String(value).trim(); } } } return ""; }; 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 : Array.isArray(data.groupList) ? data.groupList : []; const dataLevelNames = [ ...extractNameListByKeys(data, PRODUCT_NAME_LIST_FIELD_KEYS), ...extractNameListByKeys(data, PRODUCT_NAME_FIELD_KEYS), ]; const items = groups.length ? groups.flatMap((group) => { const groupItems = Array.isArray(group?.items) ? group.items : []; const groupNames = [ ...extractNameListByKeys(group, PRODUCT_NAME_LIST_FIELD_KEYS), ...extractNameListByKeys(group, PRODUCT_NAME_FIELD_KEYS), ]; return groupItems.map((item, index) => ({ ...item, // 优先使用明细自身产品名,兼容“名称数组/分隔字符串”的接口格式 productDescription: resolveProductName(item, groupNames, index), specificationModel: resolveSpecificationModel(item, group, data), salesContractNo: group?.salesContractNo || item?.salesContractNo || "", widthHeight: item?.widthHeight || "", })); }) : (Array.isArray(data.items) ? data.items : []).map((item, index) => ({ ...item, productDescription: resolveProductName(item, dataLevelNames, index), specificationModel: resolveSpecificationModel(item, data, selectedRow), 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 || ""}__${item?.specificationModel || ""}__${getOrderNo( data, row, item )}`; if (!map.has(key)) { map.set(key, { productName: item?.productDescription || "", specificationModel: item?.specificationModel || "", 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 `
| 客户名称: ${escapeHtml(data.customerName || selectedRow.customerName || "")} | 联系人: ${escapeHtml(data.contactPerson || selectedRow.contactPerson || "")} | |||||
| 发货地址: ${escapeHtml(data.deliveryAddress || data.shippingAddress || selectedRow.deliveryAddress || "")} | 联系电话: ${escapeHtml(data.contactPhone || selectedRow.contactPhone || "")} | |||||
| 序号 | 楼层编号 | 宽(弧长)*高 | 数量 | 面积 | 备注 | 加工要求 |
|---|---|---|---|---|---|---|
| 产品名称: ${escapeHtml(group.productName)}${group.specificationModel ? ` 规格型号: ${escapeHtml(group.specificationModel)}` : ""} | 订单编号: ${escapeHtml(group.orderNo)} | |||||
| 小计: | ${subQty || ""} | ${subArea ? subArea.toFixed(2) : ""} | ||||
| 暂无明细 | ||||||
| 合计: | ${totalQty || ""} | ${totalArea ? totalArea.toFixed(2) : ""} | ||||