zhangwencui
2026-04-28 b7e3bc6bbe2f6464f4f92e457212fac7ea61758d
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -13,25 +13,25 @@
                border
                row-key="tempId">
        <el-table-column label="工序名称"
                         min-width="180">
                         min-width="140">
          <template #default="{ row }">
            <span v-if="row.bom === true">{{ row.processName || "-" }}</span>
            <span v-if="row.bom === true">{{ row.operationName || "-" }}</span>
            <el-select v-else
                       v-model="row.processName"
                       v-model="row.operationName"
                       placeholder="请选择工序"
                       clearable
                       filterable
                       style="width: 100%;"
                       @change="val => handleProcessNameChange(row, val)">
              <el-option v-for="item in processOptions"
                         :key="item.id"
                         :key="item.technologyOperationId"
                         :label="item.name"
                         :value="item.name" />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="原料名称"
                         min-width="160">
                         min-width="140">
          <template #default="{ row }">
            <span v-if="row.bom === true">{{ row.materialName || "-" }}</span>
            <el-button v-else
@@ -43,17 +43,37 @@
          </template>
        </el-table-column>
        <el-table-column label="原料型号"
                         min-width="180">
                         min-width="140">
          <template #default="{ row }">
            {{ row.materialModel || "-" }}
          </template>
        </el-table-column>
        <!-- 批号多选 -->
        <el-table-column min-width="200">
          <template #header>
            <span style="color: #f56c6c; margin-right: 4px;">*</span>
            <span>批号</span>
          </template>
          <template #default="{ row }">
            <el-select v-model="row.batchNo"
                       multiple
                       collapse-tags
                       collapse-tags-indicator
                       placeholder="请选择批号"
                       style="width: 100%;">
              <el-option v-for="item in row.batchNoList"
                         :key="item"
                         :label="item"
                         :value="item" />
            </el-select>
          </template>
        </el-table-column>
        <el-table-column label="需求数量"
                         min-width="120">
          <template #default="{ row }">
            <span v-if="row.bom === true">{{ row.requiredQty ?? "-" }}</span>
            <span v-if="row.bom === true">{{ row.demandedQuantity ?? "-" }}</span>
            <el-input-number v-else
                             v-model="row.requiredQty"
                             v-model="row.demandedQuantity"
                             :min="0"
                             :precision="3"
                             :step="1"
@@ -63,7 +83,7 @@
          </template>
        </el-table-column>
        <el-table-column label="计量单位"
                         width="120">
                         width="100">
          <template #default="{ row }">
            {{ row.unit || "-" }}
          </template>
@@ -110,12 +130,18 @@
  import { computed, ref, watch } from "vue";
  import { ElMessage } from "element-plus";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js";
  import {
    findProductProcessRouteItemList,
    listMain,
  } from "@/api/productionManagement/productProcessRoute.js";
  import {
    listMaterialPickingDetail,
    listMaterialPickingBom,
    listMaterialPickingLedger,
    saveMaterialPickingLedger,
    updateMaterialPickingLedger,
  } from "@/api/productionManagement/productionOrder.js";
  import { queryList2 } from "@/api/productionManagement/productStructure.js";
  const props = defineProps({
    modelValue: { type: Boolean, default: false },
@@ -139,16 +165,22 @@
  const createMaterialRow = (row = {}) => ({
    tempId: row.id || `temp_${++materialTempId}`,
    id: row.id,
    processId: row.processId,
    productProcessId: row.productProcessId || row.processId,
    processName: row.processName || "",
    processId: row.processId || row.technologyOperationId,
    technologyOperationId: row.technologyOperationId || row.processId,
    operationName: row.operationName || "",
    bom: row.bom === true,
    materialModelId: row.materialModelId,
    materialName: row.materialName || "",
    materialModel: row.materialModel || "",
    requiredQty: Number(row.requiredQty ?? 0),
    materialModelId: row.materialModelId || row.productModelId,
    materialName: row.materialName || row.productName || "",
    materialModel: row.materialModel || row.model || "",
    demandedQuantity: Number(row.requiredQty ?? row.demandedQuantity ?? 0),
    unit: row.unit || "",
    pickQty: Number(row.pickQty ?? row.requiredQty ?? 0),
    pickQty: Number(row.pickQty ?? row.pickQuantity ?? 0),
    batchNo: row.batchNo
      ? typeof row.batchNo === "string"
        ? row.batchNo.split(",")
        : row.batchNo
      : [],
    batchNoList: row.batchNoList || [],
  });
  const getProcessOptions = async () => {
@@ -161,19 +193,20 @@
      : res?.data?.records || [];
    const processMap = new Map();
    routeList.forEach(item => {
      const processId = item.processId;
      const processName = item.processName;
      if (!processId || !processName) return;
      const key = `${processId}_${processName}`;
      const processId = item.technologyOperationId;
      const operationName = item.operationName;
      if (!processId || !operationName) return;
      const key = `${processId}_${operationName}`;
      if (!processMap.has(key)) {
        processMap.set(key, {
          id: processId,
          name: processName,
          name: operationName,
        });
      }
    });
    processOptions.value = Array.from(processMap.values());
  };
  const isDetail = ref(true);
  const loadMaterialData = async () => {
    if (!props.orderRow?.id) return;
@@ -181,23 +214,23 @@
    materialTableData.value = [];
    await getProcessOptions();
    try {
      const detailRes = await listMaterialPickingDetail({
        orderId: props.orderRow.id,
      });
      const detailRes = await listMaterialPickingDetail(props.orderRow.id);
      const detailList = Array.isArray(detailRes?.data)
        ? detailRes.data
        : detailRes?.data?.records || [];
      if (detailList.length > 0) {
        isDetail.value = true;
        materialTableData.value = detailList.map(item => createMaterialRow(item));
        return;
      } else {
        isDetail.value = false;
        const bomRes = await listMaterialPickingBom(props.orderRow.id);
        const bomList = Array.isArray(bomRes?.data)
          ? bomRes.data
          : bomRes?.data?.records || [];
        materialTableData.value = bomList.map(item => createMaterialRow(item));
        return;
      }
      const ledgerRes = await listMaterialPickingLedger({
        orderId: props.orderRow.id,
      });
      const ledgerList = Array.isArray(ledgerRes?.data)
        ? ledgerRes.data
        : ledgerRes?.data?.records || [];
      materialTableData.value = ledgerList.map(item => createMaterialRow(item));
    } finally {
      materialTableLoading.value = false;
    }
@@ -225,14 +258,16 @@
    materialTableData.value.splice(index, 1);
  };
  const handleProcessNameChange = (row, processName) => {
    const process = processOptions.value.find(item => item.name === processName);
    row.productProcessId = process?.id;
  const handleProcessNameChange = (row, operationName) => {
    const process = processOptions.value.find(
      item => item.name === operationName
    );
    row.technologyOperationId = process?.technologyOperationId;
  };
  const handleRequiredQtyChange = (row, val) => {
    const required = Number(val ?? 0);
    row.requiredQty = required;
    row.demandedQuantity = required;
    if (!row.pickQty || Number(row.pickQty) === 0) {
      row.pickQty = required;
    }
@@ -246,6 +281,8 @@
  };
  const handleMaterialProductConfirm = products => {
    console.log(products, "products");
    if (!products || products.length === 0) return;
    const index = currentMaterialSelectRowIndex.value;
    if (index < 0 || !materialTableData.value[index]) return;
@@ -257,6 +294,7 @@
      product.materialName || product.productName || product.name || "";
    row.materialModel = product.materialModel || product.model || "";
    row.unit = product.unit || product.measureUnit || "";
    row.batchNoList = product.batchNoList;
    currentMaterialSelectRowIndex.value = -1;
    materialProductDialogVisible.value = false;
  };
@@ -266,22 +304,24 @@
      return { valid: false, message: "请先新增领料数据" };
    }
    const invalidNewRow = materialTableData.value.find(
      item => item.bom !== true && (!item.processName || !item.materialName)
      item => item.bom !== true && (!item.operationName || !item.materialName)
    );
    if (invalidNewRow) {
      return { valid: false, message: "新增行的工序名称和原料名称为必填项" };
    }
    const invalidRow = materialTableData.value.find(
      item =>
        !item.processName ||
        !item.operationName ||
        !item.materialName ||
        item.requiredQty === null ||
        item.requiredQty === undefined ||
        !item.batchNo ||
        item.batchNo.length === 0 ||
        item.demandedQuantity === null ||
        item.demandedQuantity === undefined ||
        item.pickQty === null ||
        item.pickQty === undefined
    );
    if (invalidRow) {
      return { valid: false, message: "请完善工序、原料和数量后再保存" };
      return { valid: false, message: "请完善工序、原料、批号和数量后再保存" };
    }
    return { valid: true, message: "" };
  };
@@ -295,22 +335,49 @@
    }
    materialSaving.value = true;
    try {
      await saveMaterialPickingLedger({
        orderId: props.orderRow.id,
        items: materialTableData.value.map(item => ({
          id: item.id,
          processId: item.processName,
          productProcessId: item.productProcessId,
          processName: item.processName,
          bom: item.bom === true,
          materialModelId: item.materialModelId,
          materialName: item.materialName,
          materialModel: item.materialModel,
          requiredQty: item.requiredQty,
          unit: item.unit,
          pickQty: item.pickQty,
        })),
      });
      if (isDetail.value) {
        await updateMaterialPickingLedger({
          productionOrderId: props.orderRow.id,
          productionOrderPickDto: materialTableData.value.map(item => ({
            id: item.id,
            // processId: item.operationName,
            technologyOperationId: item.technologyOperationId,
            operationName: item.operationName,
            bom: item.bom === true,
            productModelId: item.materialModelId,
            // materialName: item.materialName,
            // materialModel: item.materialModel,
            demandedQuantity: item.demandedQuantity,
            unit: item.unit,
            pickQuantity: item.pickQty,
            batchNo: Array.isArray(item.batchNo)
              ? item.batchNo.join(",")
              : item.batchNo,
          })),
        });
      } else {
        await saveMaterialPickingLedger({
          productionOrderId: props.orderRow.id,
          productionOrderPickDto: materialTableData.value.map(item => ({
            id: item.id,
            // processId: item.operationName,
            technologyOperationId: item.technologyOperationId,
            operationName: item.operationName,
            bom: item.bom === true,
            productModelId: item.materialModelId,
            // materialName: item.materialName,
            // materialModel: item.materialModel,
            demandedQuantity: item.demandedQuantity,
            unit: item.unit,
            pickQuantity: item.pickQty,
            batchNo: Array.isArray(item.batchNo)
              ? item.batchNo.join(",")
              : item.batchNo,
          })),
        });
      }
      ElMessage({ message: "领料成功", type: "success" });
      emit("saved");
      dialogVisible.value = false;
    } finally {