zhangwencui
2026-04-28 b7e3bc6bbe2f6464f4f92e457212fac7ea61758d
src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue
@@ -1,44 +1,82 @@
<template>
  <div>
    <el-dialog v-model="dialogVisible" title="领料详情" width="1400px" @close="handleClose">
      <el-table v-loading="materialDetailLoading" :data="materialDetailTableData" border row-key="id">
        <el-table-column label="工序名称" prop="processName" min-width="180" />
        <el-table-column label="原料名称" prop="materialName" min-width="160" />
        <el-table-column label="原料型号" prop="materialModel" min-width="180" />
        <el-table-column label="需求数量" prop="requiredQty" min-width="110" />
        <el-table-column label="计量单位" prop="unit" width="100" />
        <el-table-column label="领用数量" prop="pickQty" min-width="110" />
        <el-table-column label="补料数量" min-width="120">
    <el-dialog v-model="dialogVisible"
               title="领料详情"
               width="1400px"
               @close="handleClose">
      <el-table v-loading="materialDetailLoading"
                :data="materialDetailTableData"
                border
                row-key="id">
        <el-table-column label="工序名称"
                         prop="operationName"
                         min-width="180" />
        <el-table-column label="原料名称"
                         prop="productName"
                         min-width="160" />
        <el-table-column label="原料型号"
                         prop="model"
                         min-width="180" />
        <el-table-column label="批号"
                         prop="batchNo"
                         min-width="150" />
        <el-table-column label="需求数量"
                         prop="demandedQuantity"
                         min-width="110" />
        <el-table-column label="计量单位"
                         prop="unit"
                         width="100" />
        <el-table-column label="领用数量"
                         prop="pickQuantity"
                         min-width="110" />
        <el-table-column label="补料数量"
                         min-width="120">
          <template #default="{ row }">
            <el-button type="primary" link @click="handleViewSupplementRecord(row)">
            <el-button type="primary"
                       link
                       @click="handleViewSupplementRecord(row)">
              {{ row.supplementQty ?? 0 }}
            </el-button>
          </template>
        </el-table-column>
        <el-table-column label="退料数量" prop="returnQty" min-width="110" />
        <el-table-column label="实际数量" prop="actualQty" min-width="110" />
        <el-table-column label="退料数量"
                         prop="returnQty"
                         min-width="110" />
        <el-table-column label="实际数量"
                         prop="actualQty"
                         min-width="110" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button
            type="warning"
            :loading="materialReturnConfirming"
            :disabled="!canOpenReturnSummary"
            @click="openReturnSummaryDialog"
          >
          <el-button 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="supplementQty" 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="supplementReason" min-width="200" />
    <el-dialog v-model="supplementRecordDialogVisible"
               title="补料记录"
               width="800px">
      <el-table v-loading="supplementRecordLoading"
                :data="supplementRecordTableData"
                border
                row-key="id">
        <el-table-column label="补料数量"
                         prop="supplementQty"
                         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="supplementReason"
                         min-width="200" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
@@ -46,18 +84,30 @@
        </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="退料汇总数量" prop="returnQtyTotal" min-width="140" />
    <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="退料汇总数量"
                         prop="returnQtyTotal"
                         min-width="140" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary" :loading="materialReturnConfirming" @click="handleReturnConfirm">确认提交</el-button>
          <el-button type="primary"
                     :loading="materialReturnConfirming"
                     @click="handleReturnConfirm">确认提交</el-button>
          <el-button @click="returnSummaryDialogVisible = false">取消</el-button>
        </span>
      </template>
@@ -66,116 +116,126 @@
</template>
<script setup>
import { computed, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { listMaterialPickingDetail, listMaterialSupplementRecord, confirmMaterialReturn } from "@/api/productionManagement/productionOrder.js";
  import { computed, ref, watch } from "vue";
  import { ElMessage } from "element-plus";
  import {
    listMaterialPickingDetail,
    listMaterialSupplementRecord,
    confirmMaterialReturn,
  } from "@/api/productionManagement/productionOrder.js";
const props = defineProps({
  modelValue: { type: Boolean, default: false },
  orderRow: { type: Object, default: null },
});
const emit = defineEmits(["update:modelValue", "confirmed"]);
const dialogVisible = computed({
  get: () => props.modelValue,
  set: val => emit("update:modelValue", val),
});
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.pickQty || 0) + Number(item.supplementQty || 0) - Number(item.actualQty || 0);
const canOpenReturnSummary = computed(() =>
  materialDetailTableData.value.some(item => calcReturnQty(item) > 0)
);
const loadDetailList = async () => {
  if (!props.orderRow?.id) return;
  materialDetailLoading.value = true;
  materialDetailTableData.value = [];
  try {
    const res = await listMaterialPickingDetail({ orderId: props.orderRow.id });
    materialDetailTableData.value = res.data || [];
  } finally {
    materialDetailLoading.value = false;
  }
};
watch(
  () => dialogVisible.value,
  visible => {
    if (visible) {
      loadDetailList();
    }
  }
);
const handleClose = () => {
  materialDetailTableData.value = [];
};
const handleViewSupplementRecord = async row => {
  if (!row?.id) return;
  supplementRecordDialogVisible.value = true;
  supplementRecordLoading.value = true;
  supplementRecordTableData.value = [];
  try {
    const res = await listMaterialSupplementRecord({ materialDetailId: row.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.materialModelId || ""}_${item.materialName || ""}_${item.materialModel || ""}_${item.unit || ""}`;
    const old = map.get(key) || {
      summaryKey: key,
      materialName: item.materialName || "",
      materialModel: item.materialModel || "",
      unit: item.unit || "",
      returnQtyTotal: 0,
    };
    old.returnQtyTotal += returnQty;
    map.set(key, old);
  const props = defineProps({
    modelValue: { type: Boolean, default: false },
    orderRow: { type: Object, default: null },
  });
  return Array.from(map.values());
};
  const emit = defineEmits(["update:modelValue", "confirmed"]);
const openReturnSummaryDialog = async () => {
  if (!canOpenReturnSummary.value) {
    ElMessage.warning("退料数量=领用数量+补料数量-实际数量,且需大于0");
    return;
  }
  returnSummaryList.value = buildReturnSummary();
  returnSummaryDialogVisible.value = true;
};
  const dialogVisible = computed({
    get: () => props.modelValue,
    set: val => emit("update:modelValue", val),
  });
const handleReturnConfirm = async () => {
  if (!props.orderRow?.id) return;
  materialReturnConfirming.value = true;
  try {
    await confirmMaterialReturn({
      orderId: props.orderRow.id,
      returnSummaryList: returnSummaryList.value,
  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.supplementQty || 0) -
    Number(item.actualQty || 0);
  const canOpenReturnSummary = computed(() =>
    materialDetailTableData.value.some(item => calcReturnQty(item) > 0)
  );
  const loadDetailList = async () => {
    if (!props.orderRow?.id) return;
    materialDetailLoading.value = true;
    materialDetailTableData.value = [];
    try {
      const res = await listMaterialPickingDetail(props.orderRow.id);
      materialDetailTableData.value = res.data || [];
    } finally {
      materialDetailLoading.value = false;
    }
  };
  watch(
    () => dialogVisible.value,
    visible => {
      if (visible) {
        loadDetailList();
      }
    }
  );
  const handleClose = () => {
    materialDetailTableData.value = [];
  };
  const handleViewSupplementRecord = async row => {
    if (!row?.id) return;
    supplementRecordDialogVisible.value = true;
    supplementRecordLoading.value = true;
    supplementRecordTableData.value = [];
    try {
      const res = await listMaterialSupplementRecord({
        materialDetailId: row.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);
    });
    returnSummaryDialogVisible.value = false;
    dialogVisible.value = false;
    emit("confirmed");
  } finally {
    materialReturnConfirming.value = false;
  }
};
    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 confirmMaterialReturn({
        orderId: props.orderRow.id,
        returnSummaryList: returnSummaryList.value,
      });
      returnSummaryDialogVisible.value = false;
      dialogVisible.value = false;
      emit("confirmed");
    } finally {
      materialReturnConfirming.value = false;
    }
  };
</script>
<style scoped lang="scss"></style>