spring
2026-05-09 dc94466f0f4abf70bbd8e7523ab1b052987eab8b
fix: 新增销售bug
已修改1个文件
125 ■■■■ 文件已修改
src/views/salesManagement/salesLedger/index.vue 125 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/salesLedger/index.vue
@@ -941,12 +941,13 @@
const normalizeStockInventoryTree = (nodes = []) => {
  const normalizeNodeValue = (node) => {
    // 后端有时会出现 id=null 的层级,这里给一个可用的 key
    if (node?.id !== null && node?.id !== undefined) return String(node.id);
    if (node?.nodeType === "batch") return String(node.batchNo ?? node.label ?? "");
    if (node?.nodeType === "customer") return String(node.customer ?? node.label ?? "");
    if (node?.nodeType === "model") return String(node.productModelId ?? node.model ?? node.label ?? "");
    return String(node.productName ?? node.label ?? "");
    // 必须用类型前缀保证全局唯一:否则会出现「产品 id=325」与「规格 productModelId=325」都规范化成 "325",
    // findNodeObjByValue 先命中规格节点,getModels 误判为选中了 model 从而不加载规格列表(Ⅰ型2号(GQ) 即此例)。
    if (node?.nodeType === "batch") return `batch:${String(node.batchNo ?? node.label ?? "").trim()}`;
    if (node?.nodeType === "customer") return `customer:${String(node.customer ?? node.label ?? "").trim()}`;
    if (node?.nodeType === "model") return `model:${String(node.productModelId ?? node.model ?? node.label ?? "").trim()}`;
    if (node?.id !== null && node?.id !== undefined) return `product:${String(node.id)}`;
    return `product:${String(node.productName ?? node.label ?? "").trim()}`;
  };
  const normalized = (list) =>
@@ -1005,6 +1006,49 @@
  return null;
};
// 规格层节点:正常有 nodeType===model;部分接口漏写 nodeType 时靠 productModelId 识别
const isInventoryModelNode = (n) => {
  if (!n) return false;
  if (n.nodeType === "model") return true;
  if (n.nodeType === "batch" || n.nodeType === "customer") return false;
  return n.productModelId !== null && n.productModelId !== undefined;
};
// 规格下拉、表单里用的是数值 productModelId,需在整棵树中按 productModelId 查找(不能依赖可能与产品 id 撞车的纯数字 value)
const findInventoryModelByProductModelId = (nodes = [], productModelId) => {
  const target = String(productModelId ?? "").trim();
  if (!target) return null;
  for (const node of nodes || []) {
    if (isInventoryModelNode(node) && String(node.productModelId ?? "").trim() === target) {
      return node;
    }
    const found = findInventoryModelByProductModelId(node.children || [], productModelId);
    if (found) return found;
  }
  return null;
};
// 库存树为「产品 → 子产品 → 规格型号 → 批次…」,选中大类或中间产品节点时需收集整棵子树里的规格节点
const collectDescendantModelNodes = (root) => {
  if (!root) return [];
  const models = [];
  const seen = new Set();
  const walk = (n) => {
    if (!n) return;
    if (isInventoryModelNode(n)) {
      const key = String(n.productModelId ?? n.value ?? "");
      if (key && !seen.has(key)) {
        seen.add(key);
        models.push(n);
      }
      return;
    }
    for (const c of n.children || []) walk(c);
  };
  walk(root);
  return models;
};
// 获取库存树(用于产品大类/规格型号联动)
const getProductOptions = async () => {
  // 返回 Promise,便于在编辑产品时等待加载完成
@@ -1019,20 +1063,40 @@
};
// 获取tree子数据(先选产品,再选规格型号)
const getModels = (value) => {
  if (value === null || value === undefined || value === "") {
    modelOptions.value = [];
    productForm.value.productModelId = null;
    productForm.value.specificationModel = "";
    productForm.value.uidNo = "";
    productForm.value.unit = "";
    productForm.value.batchNo = "";
    productForm.value.customer = "";
    productForm.value.taxInclusiveUnitPrice = "";
    productForm.value.taxInclusiveTotalPrice = "";
    productForm.value.taxExclusiveTotalPrice = "";
    batchNoOptions.value = [];
    supplierOptions.value = [];
    batchNodeByBatchNo = new Map();
    return;
  }
  const node = findNodeObjByValue(stockInventoryAllTree, value);
  if (!node) return;
  if (node.nodeType !== "product") return;
  // 树里可选的是产品/子目录;仅排除真正的叶语义节点(部分数据漏写 nodeType:product 时不能卡死)
  if (node.nodeType === "model" || node.nodeType === "batch" || node.nodeType === "customer") return;
  // 选择产品后,重置下游字段
  productForm.value.productCategory = node.label;
  modelOptions.value = (node.children || [])
      .filter((c) => c.nodeType === "model")
      .map((m) => ({
        id: m.value,
        model: m.model ?? m.label ?? "",
        unit: m.unit ?? "",
        uidNo: m.uidNo ?? m.identifierCode ?? "",
      }));
  const modelNodes = collectDescendantModelNodes(node);
  modelOptions.value = modelNodes.map((m) => ({
    id:
        m.productModelId !== null && m.productModelId !== undefined
            ? m.productModelId
            : Number(String(m.value).replace(/^model:/, "")) || m.value,
    model: m.model ?? m.label ?? "",
    unit: m.unit ?? "",
    uidNo: m.uidNo ?? m.identifierCode ?? "",
  }));
  productForm.value.productModelId = null;
  productForm.value.specificationModel = "";
@@ -1052,13 +1116,20 @@
// 规格型号选择后:回显 UID,并生成“批号下拉”
const getProductModel = (value) => {
  const modelNode = findNodeObjByValue(stockInventoryAllTree, value);
  if (!modelNode || modelNode.nodeType !== "model") return;
  if (value === null || value === undefined || value === "") return;
  let modelNode = findNodeObjByValue(stockInventoryAllTree, value);
  if (!modelNode || !isInventoryModelNode(modelNode)) {
    modelNode = findInventoryModelByProductModelId(stockInventoryAllTree, value);
  }
  if (!modelNode || !isInventoryModelNode(modelNode)) return;
  const prevBatchNo = productForm.value.batchNo;
  const prevCustomer = productForm.value.customer;
  productForm.value.productModelId = modelNode.value;
  productForm.value.productModelId =
      modelNode.productModelId !== null && modelNode.productModelId !== undefined
          ? modelNode.productModelId
          : Number(String(modelNode.value).replace(/^model:/, "")) || null;
  productForm.value.specificationModel = modelNode.model ?? modelNode.label ?? "";
  // 有些接口/树数据里可能不包含 unit,这种情况下不要覆盖编辑时已回显的值
  const nextUnit = modelNode.unit ?? "";
@@ -1446,14 +1517,16 @@
      const categoryKey = findNodeIdByLabel(productOptions.value, productForm.value.productCategory);
      if (categoryKey) {
        const categoryNode = findNodeObjByValue(stockInventoryAllTree, categoryKey);
        const models = (categoryNode?.children || [])
            .filter((n) => n.nodeType === "model")
            .map((m) => ({
              id: m.value,
              model: m.model ?? m.label ?? "",
              unit: m.unit ?? "",
              uidNo: m.uidNo ?? m.identifierCode ?? "",
            }));
        const modelNodes = collectDescendantModelNodes(categoryNode);
        const models = modelNodes.map((m) => ({
          id:
              m.productModelId !== null && m.productModelId !== undefined
                  ? m.productModelId
                  : Number(String(m.value).replace(/^model:/, "")) || m.value,
          model: m.model ?? m.label ?? "",
          unit: m.unit ?? "",
          uidNo: m.uidNo ?? m.identifierCode ?? "",
        }));
        modelOptions.value = models;
        // 根据当前规格型号回显