gongchunyi
2026-09-18 4dee4cb5b31a7f1dcd2c7b3d72f74b78d2fe3f29
src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -6,12 +6,19 @@
               @close="handleClose">
      <div class="material-toolbar">
        <el-button type="primary"
                   plain
                   @click="handleAddPurchase">新增采购</el-button>
        <el-button type="primary"
                   @click="handleAddMaterialRow">新增</el-button>
      </div>
      <el-table v-loading="materialTableLoading"
                :data="materialTableData"
                border
                @selection-change="handleMaterialSelectionChange"
                row-key="tempId">
        <el-table-column align="center"
                         type="selection"
                         width="55" />
        <el-table-column label="工序名称"
                         min-width="100">
          <template #default="{ row }">
@@ -80,13 +87,20 @@
                             @change="val => handleRequiredQtyChange(row, val)" />
          </template>
        </el-table-column>
        <el-table-column label="库存余量"
                         min-width="90">
          <template #default="{ row }">
            <span v-if="row.stockQty === null">-</span>
            <span v-else>{{ stripTrailingZeros(row.stockQty) }}</span>
          </template>
        </el-table-column>
        <el-table-column label="已领数量"
                         min-width="90">
          <template #default="{ row }">
            {{ stripTrailingZeros(row.pickedQty) ?? 0 }}
          </template>
        </el-table-column>
        <el-table-column label="剩余可领"
        <el-table-column label="剩余需领"
                         min-width="90">
          <template #default="{ row }">
            <span :class="{ 'remaining-empty': Number(row.remainingQty) <= 0 }">
@@ -136,6 +150,8 @@
    <ProductSelectDialog v-model="materialProductDialogVisible"
                         @confirm="handleMaterialProductConfirm"
                         single />
    <PurchaseLedgerAddDialog ref="purchaseLedgerAddDialogRef"
                             v-model="purchaseLedgerDialogVisible" />
    <!-- request-url="/stockInventory/rawMaterials" -->
  </div>
</template>
@@ -144,7 +160,9 @@
  import { computed, ref, watch } from "vue";
  import { ElMessage } from "element-plus";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  import PurchaseLedgerAddDialog from "./PurchaseLedgerAddDialog.vue";
  import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js";
  import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js";
  import {
    listMaterialPickingDetail,
    listMaterialPickingBom,
@@ -166,6 +184,9 @@
  const materialTableLoading = ref(false);
  const materialSaving = ref(false);
  const materialTableData = ref([]);
  const selectedMaterialRows = ref([]);
  const purchaseLedgerDialogVisible = ref(false);
  const purchaseLedgerAddDialogRef = ref(null);
  const isSaveDisabled = computed(() => {
    if (materialTableLoading.value) return true;
@@ -222,6 +243,7 @@
      returnedQty,
      remainingQty,
      pickQty: 0,
      stockQty: null,
      batchNo: [],
      batchNoList,
    };
@@ -349,6 +371,17 @@
          })
        );
      });
      // 先查出库存余量再入表,保证首次打开即展示(rows 尚未转为响应式代理,
      // 入表后再改原始对象不会触发视图更新)
      await loadStockQtyForRows(rows);
      // 打开领料台账默认:自动选中第一个可用批号,领用数量=需求数量
      rows.forEach(row => {
        const batchNoList = Array.isArray(row.batchNoList) ? row.batchNoList : [];
        if (batchNoList.length > 0) {
          row.batchNo = [batchNoList[0]];
          row.pickQty = toNumber(row.demandedQuantity);
        }
      });
      materialTableData.value = rows;
    } finally {
      materialTableLoading.value = false;
@@ -366,11 +399,73 @@
  const handleClose = () => {
    materialTableData.value = [];
    selectedMaterialRows.value = [];
    currentMaterialSelectRowIndex.value = -1;
  };
  // 按型号汇总所有批次的原料可用库存余量
  const loadStockQtyForRows = async rows => {
    const modelIds = [
      ...new Set(rows.map(row => row.materialModelId).filter(Boolean)),
    ];
    const stockMap = new Map();
    await Promise.all(
      modelIds.map(async modelId => {
        try {
          const res = await getStockInventoryByModelId(modelId);
          const list = Array.isArray(res?.data)
            ? res.data
            : res?.data?.records || res?.data?.rows || [];
          const total = list.reduce(
            (sum, item) => sum + toNumber(item?.qualitity ?? item?.quantity),
            0
          );
          stockMap.set(modelId, roundQuantity(total));
        } catch (e) {
          stockMap.set(modelId, 0);
        }
      })
    );
    rows.forEach(row => {
      if (!row.materialModelId) {
        row.stockQty = null;
        return;
      }
      row.stockQty = stockMap.has(row.materialModelId)
        ? stockMap.get(row.materialModelId)
        : 0;
    });
  };
  const handleAddMaterialRow = () => {
    materialTableData.value.push(createMaterialRow());
  };
  const handleMaterialSelectionChange = selection => {
    selectedMaterialRows.value = selection;
  };
  const handleAddPurchase = () => {
    if (selectedMaterialRows.value.length === 0) {
      ElMessage.warning("请选择需要新增采购的原料行");
      return;
    }
    const rowsWithModel = selectedMaterialRows.value.filter(
      row => row.materialModelId
    );
    if (rowsWithModel.length === 0) {
      ElMessage.warning("所选行缺少原料型号,请先选择原料后再新增采购");
      return;
    }
    const productRows = rowsWithModel.map(row => ({
      productModelId: row.materialModelId,
      productCategory: row.materialName,
      specificationModel: row.materialModel,
      unit: row.unit,
      quantity: row.demandedQuantity,
      isChecked: false,
    }));
    purchaseLedgerAddDialogRef.value?.open(productRows);
  };
  const handleDeleteMaterialRow = index => {
@@ -416,6 +511,7 @@
    row.batchNo = [];
    currentMaterialSelectRowIndex.value = -1;
    materialProductDialogVisible.value = false;
    loadStockQtyForRows([row]);
  };
  const validateMaterialRows = () => {