src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -4,28 +4,28 @@
               title="领料详情"
               width="1400px"
               @close="handleClose">
      <div class="material-detail-tip">
        展示该生产订单下所有工序的领料记录。
      </div>
      <el-table v-loading="materialDetailLoading"
                :data="materialDetailTableData"
                border
                row-key="id">
        <el-table-column label="工序名称"
                         prop="operationName"
                         min-width="180" />
                         min-width="160" />
        <el-table-column label="工单编号"
                         prop="workOrderNo"
                         min-width="150" />
        <el-table-column label="原料名称"
                         prop="productName"
                         prop="materialName"
                         min-width="160" />
        <el-table-column label="原料型号"
                         prop="model"
                         prop="materialModel"
                         min-width="180" />
        <el-table-column label="批号"
                         prop="batchNo"
                         min-width="150" />
        <el-table-column label="需求数量"
                         min-width="110">
          <template #default="{ row }">
            {{ stripTrailingZeros(row.demandedQuantity) }}
          </template>
        </el-table-column>
        <el-table-column label="计量单位"
                         prop="unit"
                         width="100" />
@@ -35,105 +35,28 @@
            {{ stripTrailingZeros(row.pickQuantity) }}
          </template>
        </el-table-column>
        <el-table-column label="补料数量"
                         min-width="120">
          <template #default="{ row }">
            <el-button type="primary"
                       link
                       @click="handleViewSupplementRecord(row)">
              {{ stripTrailingZeros(row.feedingQty) ?? 0 }}
            </el-button>
          </template>
        </el-table-column>
        <el-table-column label="退料数量"
                         min-width="110">
          <template #default="{ row }">
            {{ stripTrailingZeros(row.returnQty) ?? 0 }}
            {{ stripTrailingZeros(row.returnQty) || 0 }}
          </template>
        </el-table-column>
        <el-table-column label="实际数量"
                         min-width="140">
        <el-table-column label="净领料数量"
                         min-width="130">
          <template #default="{ row }">
            <el-input-number v-model="row.actualQty"
                             :min="0"
                             :step="1"
                             controls-position="right"
                             placeholder="输入实际数量"
                             :formatter="value => stripTrailingZeros(value)"
                             :parser="value => parseFloat(value) || 0"
                             style="width: 100%;"
                             :disabled="row.returned || orderRow?.end"
                             @change="val => handleActualQtyChange(row, val)" />
            {{ stripTrailingZeros(row.effectiveQuantity) || 0 }}
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button v-if="!orderRow?.end"
                     type="warning"
                     :loading="materialReturnConfirming"
                     :disabled="!canOpenReturnSummary"
                     @click="openReturnSummaryDialog">
            退料确认
          </el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
        </span>
      </template>
    </el-dialog>
    <el-dialog v-model="supplementRecordDialogVisible"
               title="补料记录"
               width="800px">
      <el-table v-loading="supplementRecordLoading"
                :data="supplementRecordTableData"
                border
                row-key="id">
        <el-table-column label="补料数量"
                         prop="pickQuantity"
                         min-width="120" />
        <el-table-column label="补料人"
                         prop="supplementUserName"
                         min-width="120" />
        <el-table-column label="补料日期"
                         prop="supplementTime"
                         min-width="160" />
        <el-table-column label="补料原因"
                         prop="feedingReason"
                         min-width="200" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="supplementRecordDialogVisible = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>
    <el-dialog v-model="returnSummaryDialogVisible"
               title="退料汇总确认"
               width="900px">
      <el-table :data="returnSummaryList"
                border
                row-key="summaryKey">
        <el-table-column label="原料名称"
                         prop="materialName"
                         min-width="180" />
        <el-table-column label="原料型号"
                         prop="materialModel"
                         min-width="180" />
        <el-table-column label="计量单位"
                         prop="unit"
                         min-width="100" />
        <el-table-column label="退料汇总数量"
                         min-width="140">
        <el-table-column label="领料时间"
                         min-width="120">
          <template #default="{ row }">
            {{ stripTrailingZeros(row.returnQtyTotal) }}
            {{ formatDate(row.createTime) }}
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     :loading="materialReturnConfirming"
                     @click="handleReturnConfirm">确认提交</el-button>
          <el-button @click="returnSummaryDialogVisible = false">取消</el-button>
          <el-button @click="dialogVisible = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>
@@ -142,12 +65,9 @@
<script setup>
  import { computed, ref, watch } from "vue";
  import { ElMessage } from "element-plus";
  import {
    listMaterialPickingDetail,
    listMaterialSupplementRecord,
    updateMaterialPickingLedger,
  } from "@/api/productionManagement/productionOrder.js";
  import { listMaterialPickingDetail } from "@/api/productionManagement/productionOrder.js";
  const formatDate = value => (value ? String(value).slice(0, 10) : "-");
  const stripTrailingZeros = val => {
    const str = String(val ?? "");
@@ -170,21 +90,6 @@
  const materialDetailLoading = ref(false);
  const materialDetailTableData = ref([]);
  const materialReturnConfirming = ref(false);
  const supplementRecordDialogVisible = ref(false);
  const supplementRecordLoading = ref(false);
  const supplementRecordTableData = ref([]);
  const returnSummaryDialogVisible = ref(false);
  const returnSummaryList = ref([]);
  const calcReturnQty = item =>
    Number(item.pickQuantity || 0) +
    Number(item.feedingQty || 0) -
    Number(item.actualQty || 0);
  const canOpenReturnSummary = computed(() =>
    materialDetailTableData.value.some(
      item => item.returned !== true && calcReturnQty(item) > 0
    )
  );
  const loadDetailList = async () => {
    if (!props.orderRow?.id) return;
@@ -192,12 +97,15 @@
    materialDetailTableData.value = [];
    try {
      const res = await listMaterialPickingDetail(props.orderRow.id);
      materialDetailTableData.value = (res.data || []).map(item => ({
      const records = Array.isArray(res.data) ? res.data : res.data?.records || [];
      materialDetailTableData.value = records.map(item => ({
        ...item,
        actualQty:
          item.actualQty ??
          Number(item.pickQuantity || 0) + Number(item.feedingQty || 0),
        operationName: item.operationName || item.processName || "",
        materialName: item.productName || item.materialName || "",
        materialModel: item.model || item.materialModel || "",
        pickQuantity: item.pickQuantity ?? item.quantity ?? 0,
        returnQty: item.returnQty ?? 0,
        effectiveQuantity: item.effectiveQuantity ?? 0,
      }));
    } finally {
      materialDetailLoading.value = false;
@@ -216,87 +124,12 @@
  const handleClose = () => {
    materialDetailTableData.value = [];
  };
  const handleActualQtyChange = (row, val) => {
    row.returnQty = calcReturnQty(row);
  };
  const handleViewSupplementRecord = async row => {
    if (!row?.id) return;
    supplementRecordDialogVisible.value = true;
    supplementRecordLoading.value = true;
    supplementRecordTableData.value = [];
    try {
      const res = await listMaterialSupplementRecord({
        pickId: row.id,
        productionOrderId: props.orderRow.id,
      });
      supplementRecordTableData.value = res.data || [];
    } finally {
      supplementRecordLoading.value = false;
    }
  };
  const buildReturnSummary = () => {
    const map = new Map();
    materialDetailTableData.value.forEach(item => {
      const returnQty = calcReturnQty(item);
      if (returnQty <= 0) return;
      const key = `${item.productModelId || ""}_${item.productName || ""}_${
        item.model || ""
      }_${item.unit || ""}`;
      const old = map.get(key) || {
        summaryKey: key,
        materialName: item.productName || "",
        materialModel: item.model || "",
        unit: item.unit || "",
        returnQtyTotal: 0,
      };
      old.returnQtyTotal += returnQty;
      map.set(key, old);
    });
    return Array.from(map.values());
  };
  const openReturnSummaryDialog = async () => {
    if (!canOpenReturnSummary.value) {
      ElMessage.warning("退料数量=领用数量+补料数量-实际数量,且需大于0");
      return;
    }
    returnSummaryList.value = buildReturnSummary();
    returnSummaryDialogVisible.value = true;
  };
  const handleReturnConfirm = async () => {
    if (!props.orderRow?.id) return;
    materialReturnConfirming.value = true;
    try {
      await updateMaterialPickingLedger({
        productionOrderId: props.orderRow.id,
        productionOrderPickDto: materialDetailTableData.value.map(item => ({
          id: item.id,
          technologyOperationId: item.technologyOperationId,
          operationName: item.operationName,
          bom: item.bom === true,
          productModelId: item.productModelId,
          demandedQuantity: item.demandedQuantity,
          unit: item.unit,
          pickQuantity: item.pickQuantity,
          batchNo: item.batchNo,
          feedingQty: item.feedingQty,
          returnQty: item.returnQty,
          actualQty: item.actualQty,
          feedingReason: item.feedingReason,
          returned: true,
        })),
      });
      returnSummaryDialogVisible.value = false;
      dialogVisible.value = false;
      emit("confirmed");
    } finally {
      materialReturnConfirming.value = false;
    }
  };
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
  .material-detail-tip {
    margin-bottom: 12px;
    color: #606266;
    font-size: 13px;
  }
</style>