昨天 c7ac44a1004dcf791fe7282368f1f657e035c961
src/views/salesManagement/returnOrder/components/formDia.vue
@@ -122,7 +122,7 @@
                placeholder="请输入" 
              />
            </template>
            <template #action="{ row, index }">
            <template #action="{ index }">
              <el-button type="danger" link @click="deleteRow(index)">删除</el-button>
            </template>
          </PIMTable>
@@ -145,10 +145,12 @@
        row-key="id"
      >
        <el-table-column align="center" type="selection" width="55" />
        <el-table-column align="center" prop="outboundBatches" label="出库单号" />
        <el-table-column align="center" prop="batchNo" label="批次号" />
        <el-table-column align="center" prop="productCategory" label="产品大类" />
        <el-table-column align="center" prop="specificationModel" label="规格型号" />
        <el-table-column align="center" prop="unit" label="单位" />
        <el-table-column align="center" prop="quantity" label="总数量" />
        <el-table-column align="center" prop="stockOutNum" label="总数量" />
        <el-table-column align="center" prop="unQuantity" label="未退货数量" />
        <el-table-column align="center" label="已退货数量">
          <template #default="{ row }">{{ calcAlreadyReturned(row) }}</template>
@@ -208,17 +210,19 @@
const { form, rules } = toRefs(data);
const calcAlreadyReturned = (row) => {
  const total = Number(row?.quantity ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0);
  const total = Number(row?.stockOutNum ?? row?.totalQuantity ?? row?.totalReturnNum ?? 0);
  const un = Number(row?.unQuantity ?? 0);
  if (!Number.isFinite(total) || !Number.isFinite(un)) return 0;
  return Math.max(total - un, 0);
};
const tableColumn = ref([
  {align: "center", label: "出库单号", prop: "outboundBatches" },
  {align: "center", label: "批次号", prop: "batchNo" },
  {align: "center", label: "产品大类", prop: "productCategory" },
  {align: "center", label: "规格型号", prop: "specificationModel" },
  {align: "center", label: "单位", prop: "unit", width: 80 },
  {align: "center", label: "总数量", prop: "quantity", width: 120 },
  {align: "center", label: "总数量", prop: "stockOutNum", width: 120 },
  {align: "center", label: "已退货数量", prop: "totalReturnNum", width: 120 },
  {align: "center", label: "未退货数量", prop: "unQuantity", width: 120 },
  {align: "center", label: "退货数量", prop: "returnQuantity", dataType: "slot", slot: "returnQuantity", width: 120 },
@@ -238,8 +242,164 @@
  tableData.value.splice(index, 1);
};
const sameKey = (a, b) => a != null && b != null && String(a) === String(b);
/** 接口可能拆成 shippingProductVoList / productDtoData 两份,只取其一会缺批次、数量等字段 */
const mergeShippingProductLists = (data) => {
  const lists = [data?.shippingProductVoList, data?.productDtoData].filter(Array.isArray);
  if (!lists.length) return [];
  const map = new Map();
  for (const list of lists) {
    for (const p of list) {
      if (p == null) continue;
      const key = p.id != null ? String(p.id) : null;
      if (!key) continue;
      const prev = map.get(key);
      map.set(key, prev ? { ...prev, ...p } : { ...p });
    }
  }
  return Array.from(map.values());
};
const pickShippingLine = (normalized) => {
  const pid = normalized?.returnSaleLedgerProductId ?? normalized?.id;
  const sid = normalized?.stockOutRecordId ?? normalized?.shippingProductId;
  const direct = availableProducts.value.find(
    (p) =>
      sameKey(p?.id, pid) ||
      sameKey(p?.stockOutRecordId, pid) ||
      sameKey(p?.id, sid) ||
      sameKey(p?.stockOutRecordId, sid)
  );
  if (direct) return direct;
  const pmid = normalized?.productModelId;
  if (pmid == null || pmid === "") return undefined;
  const candidates = availableProducts.value.filter((p) => sameKey(p?.productModelId, pmid));
  if (!candidates.length) return undefined;
  if (candidates.length === 1) return candidates[0];
  const spec = String(normalized?.specificationModel ?? normalized?.model ?? "");
  if (spec) {
    const hit = candidates.find((p) => {
      const ps = String(p?.specificationModel ?? p?.model ?? "");
      return ps && ps === spec;
    });
    if (hit) return hit;
  }
  return candidates[0];
};
const isEmptyText = (v) => v === "" || v == null || v === undefined;
const firstFiniteNumber = (...vals) => {
  for (const v of vals) {
    if (v === "" || v == null || v === undefined) continue;
    const n = Number(v);
    if (Number.isFinite(n)) return n;
  }
  return undefined;
};
const firstNonEmptyText = (...vals) => {
  const hit = vals.find((v) => !isEmptyText(v));
  return hit === undefined ? "" : hit;
};
/** 详情接口字段常不全;{...product,...normalized} 会被 normalized 里的空串盖掉出库行上的展示字段 */
const mergeShippingLineWithDetail = (product, normalized) => {
  const row = { ...product, ...normalized };
  row.outboundBatches = firstNonEmptyText(
    row.outboundBatches,
    product?.outboundBatches,
    product?.shippingNo,
    product?.outboundNo,
    normalized?.outboundBatches,
    normalized?.outboundNo,
    normalized?.shippingNo
  );
  row.batchNo = firstNonEmptyText(
    row.batchNo,
    product?.batchNo,
    product?.batchNumber,
    product?.lotNo,
    product?.batchCode,
    product?.shippingBatchNo,
    normalized?.batchNo,
    normalized?.batchNumber,
    normalized?.lotNo,
    normalized?.shippingBatchNo
  );
  const stock = firstFiniteNumber(
    row.stockOutNum,
    product?.stockOutNum,
    product?.totalQuantity,
    product?.shippingQuantity,
    product?.deliveryQuantity,
    product?.quantity,
    product?.outQuantity,
    normalized?.stockOutNum,
    normalized?.totalQuantity,
    normalized?.shippingQuantity,
    normalized?.deliveryQuantity
  );
  if (stock !== undefined) row.stockOutNum = stock;
  const un = firstFiniteNumber(
    row.unQuantity,
    product?.unQuantity,
    product?.remainingQuantity,
    product?.noReturnQuantity,
    product?.canReturnQuantity,
    product?.availableReturnNum,
    normalized?.unQuantity,
    normalized?.remainingQuantity,
    normalized?.noReturnQuantity,
    normalized?.canReturnQuantity
  );
  if (un !== undefined) row.unQuantity = un;
  else {
    const s = Number(row.stockOutNum);
    const ret = Number(row.totalReturnNum ?? 0);
    if (Number.isFinite(s) && s >= 0 && Number.isFinite(ret) && ret >= 0) {
      row.unQuantity = Math.max(0, s - ret);
    }
  }
  const returned = firstFiniteNumber(
    row.totalReturnNum,
    product?.totalReturnNum,
    product?.totalReturnedNum,
    normalized?.totalReturnNum,
    normalized?.totalReturnedNum
  );
  if (returned !== undefined) row.totalReturnNum = returned;
  else if (isEmptyText(row.totalReturnNum)) row.totalReturnNum = 0;
  if (isEmptyText(row.unit)) {
    row.unit = firstNonEmptyText(product?.unit, normalized?.unit);
  }
  if (isEmptyText(row.productCategory)) {
    row.productCategory = firstNonEmptyText(
      normalized?.productCategory,
      normalized?.productName,
      product?.productCategory,
      product?.productName
    );
  }
  if (isEmptyText(row.specificationModel)) {
    row.specificationModel = firstNonEmptyText(
      normalized?.specificationModel,
      normalized?.model,
      product?.specificationModel,
      product?.model
    );
  }
  return row;
};
const normalizeDetailRow = (raw) => {
  const productId = raw?.returnSaleLedgerProductId ?? raw?.saleLedgerProductId ?? raw?.id;
  const ledgerId =
    raw?.returnSaleLedgerProductId ??
    raw?.saleLedgerProductId ??
    raw?.stockOutRecordId ??
    raw?.shippingProductId;
  const productId = ledgerId ?? raw?.id;
  const returnSaleProductId = raw?.returnSaleProductId ?? raw?.id;
  const num = Number(raw?.num ?? raw?.returnQuantity ?? 0);
  return {
@@ -248,6 +408,29 @@
    returnSaleProductId,
    returnSaleLedgerProductId: productId,
    productModelId: raw?.productModelId,
    stockOutRecordId: raw?.stockOutRecordId,
    shippingProductId: raw?.shippingProductId,
    productCategory: raw?.productCategory ?? raw?.productName ?? raw?.productTypeName ?? "",
    specificationModel: raw?.specificationModel ?? raw?.model ?? raw?.specModel ?? "",
    outboundBatches: raw?.outboundBatches ?? raw?.outboundNo ?? raw?.shippingNo,
    batchNo:
      raw?.batchNo ??
      raw?.batchNumber ??
      raw?.lotNo ??
      raw?.batchCode ??
      raw?.shippingBatchNo,
    stockOutNum:
      raw?.stockOutNum ??
      raw?.totalQuantity ??
      raw?.shippingQuantity ??
      raw?.deliveryQuantity ??
      raw?.quantity,
    totalReturnNum: raw?.totalReturnNum ?? raw?.totalReturnedNum,
    unQuantity:
      raw?.unQuantity ??
      raw?.remainingQuantity ??
      raw?.noReturnQuantity ??
      raw?.canReturnQuantity,
    num,
    returnQuantity: Number.isFinite(num) ? num : 0,
    price: Number(raw?.taxInclusiveUnitPrice ?? raw?.price ?? 0),
@@ -259,7 +442,6 @@
const setFormForEdit = async (row) => {
  const res = await returnManagementGetById({ returnManagementId: row?.id });
  console.log("res", res);
  const detail = res?.data ?? res ?? {};
  Object.assign(form.value, detail);
@@ -281,11 +463,18 @@
  tableData.value = Array.isArray(list)
    ? list.map((raw) => {
        const normalized = normalizeDetailRow(raw);
        const product = availableProducts.value.find((p) => p.id === normalized.id);
        return product ? { ...product, ...normalized } : normalized;
        const product = pickShippingLine(normalized);
        return product ? mergeShippingLineWithDetail(product, normalized) : normalized;
      })
    : [];
  const headerShipNo = detail?.shippingNo ?? form.value?.shippingNo;
  if (headerShipNo && Array.isArray(tableData.value) && tableData.value.length) {
    tableData.value = tableData.value.map((r) =>
      isEmptyText(r.outboundBatches) ? { ...r, outboundBatches: headerShipNo } : r
    );
  }
  calculateTotalRefund();
};
@@ -320,7 +509,7 @@
  proxy.$refs["formRef"].validate(valid => {
    if (!valid) return;
    const returnSaleProducts = (tableData.value || []).map(el => ({
      returnSaleLedgerProductId: el.returnSaleLedgerProductId ?? el.id,
      stockOutRecordId: el.returnSaleLedgerProductId ?? el.id,
      productModelId: el.productModelId,
      unit: el.unit,
      num: Number(el.num ?? el.returnQuantity ?? 0),
@@ -419,8 +608,7 @@
    // If backend returns project info, set it
    if (res.data.projectId) form.value.projectId = res.data.projectId;
    
    // Store available products for selection
    availableProducts.value = res.data.productDtoData || [];
    availableProducts.value = mergeShippingProductLists(res.data);
    if (clearTable) tableData.value = [];
  }
};
@@ -457,9 +645,9 @@
};
const calculateRowAmount = (row) => {
  const quantity = Number(row.returnQuantity || 0);
  const stockOutNum = Number(row.returnQuantity || 0);
  const price = Number(row.price || 0);
  row.amount = (quantity * price).toFixed(2);
  row.amount = (stockOutNum * price).toFixed(2);
};
const calculateTotalRefund = () => {
@@ -511,10 +699,11 @@
        amount: "0.00",
        isQuality: 2,
        remark: "",
        productCategory: product.productCategory ?? product.productName ?? "",
        productName: product.productName,
        specificationModel: product.specificationModel,
        specificationModel: product.specificationModel ?? product.model ?? "",
        unit: product.unit,
        quantity: product.quantity,
        stockOutNum: product.stockOutNum,
        totalReturnNum: product.totalReturnNum,
        unQuantity: product.unQuantity
      });