yyb
2 天以前 0af12dc242cd9b771b8ea7c2cff6ace54441718f
src/views/salesManagement/salesLedger/components/salesDeliveryPrint.js
@@ -1,3 +1,5 @@
import QRCode from "qrcode";
const PRINT_TITLE = "销售发货单";
const escapeHtml = (value) =>
@@ -28,6 +30,69 @@
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 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 splitItemsByPage = (items, pageSize) => {
  const list = Array.isArray(items) ? items : [];
  if (list.length === 0) return [[]];
@@ -40,17 +105,36 @@
const normalizeInvoiceData = (raw, selectedRow) => {
  const data = raw ?? {};
  const groups = Array.isArray(data.groups) ? data.groups : [];
  if (!groups.length) return data;
  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.flatMap((group) =>
    (Array.isArray(group?.items) ? group.items : []).map((item) => ({
      ...item,
      productDescription: group?.productName || item?.productDescription || "",
      salesContractNo: group?.salesContractNo || item?.salesContractNo || "",
      widthHeight: item?.widthHeight || "",
    }))
  );
  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),
          salesContractNo: group?.salesContractNo || item?.salesContractNo || "",
          widthHeight: item?.widthHeight || "",
        }));
      })
    : (Array.isArray(data.items) ? data.items : []).map((item, index) => ({
        ...item,
        productDescription: resolveProductName(item, dataLevelNames, index),
        widthHeight: item?.widthHeight || "",
      }));
  return {
    ...data,
@@ -105,12 +189,33 @@
    })
    .join("");
export const printSalesDeliveryNote = (rawData, selectedRow = {}) => {
export const printSalesDeliveryNote = async (rawData, selectedRow = {}, ledgerIds = null) => {
  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 ids =
    Array.isArray(ledgerIds) && ledgerIds.length > 0
      ? ledgerIds
      : selectedRow?.id !== undefined && selectedRow?.id !== null && selectedRow?.id !== ""
        ? [selectedRow.id]
        : [];
  const shipmentRef = String(
    data.shipmentNo || data.deliveryNo || data.externalOrderNo || selectedRow?.expressNumber || ""
  ).trim();
  const qrPayload = JSON.stringify({
    type: "FH",
    shipmentNo: shipmentRef,
    ledgerIds: ids,
  });
  let qrDataUrl = "";
  try {
    qrDataUrl = await QRCode.toDataURL(qrPayload, { width: 160, margin: 1 });
  } catch {
    qrDataUrl = "";
  }
  const printWindow = window.open("", "_blank", "width=1200,height=900");
  if (!printWindow) {
@@ -146,17 +251,32 @@
        font-size: 12px;
        margin-right: 8mm;
        margin-bottom: 1px;
        position: relative;
        top: 6mm;
      }
      .head-mid {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
        align-items: center;
        margin-bottom: 2px;
        margin-top: 6mm;
        margin-bottom: 0;
        position: relative;
      }
      .head-mid-left { font-size: 13px; text-align: left; }
      .head-mid-title { font-size: 20px; font-weight: 700; text-align: center; }
      .head-mid-title-wrap { grid-column: 2; }
      .head-mid-title { font-size: 20px; font-weight: 700; text-align: center; line-height: 1; }
      .head-qr {
        width: 18mm;
        height: 18mm;
        object-fit: contain;
        display: block;
        position: absolute;
        left: calc(50% + 30mm);
        top: calc(50% - 4mm);
        transform: translateY(-50%);
      }
      .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; }
      table { width: 100%; margin-top: 3mm; 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; }
@@ -185,7 +305,10 @@
      </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-title-wrap">
          <div class="head-mid-title">销售发货单</div>
        </div>
        ${qrDataUrl ? `<img class="head-qr" src="${qrDataUrl}" alt="二维码" />` : ""}
        <div class="head-mid-right">发货单号: ${escapeHtml(data.shipmentNo || data.deliveryNo || "")}</div>
      </div>
      <table>