| | |
| | | <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)"> |
| | | {{ row.supplementQty ?? 0 }} |
| | | <el-button type="primary" |
| | | link |
| | | @click="handleViewSupplementRecord(row)"> |
| | | {{ row.feedingQty ?? 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="退料数量" |
| | | min-width="110"> |
| | | <template #default="{ row }"> |
| | | {{ row.returnQty ?? 0 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="实际数量" |
| | | min-width="140"> |
| | | <template #default="{ row }"> |
| | | <el-input-number v-model="row.actualQty" |
| | | :min="0" |
| | | :precision="3" |
| | | :step="1" |
| | | controls-position="right" |
| | | placeholder="输入实际数量" |
| | | style="width: 100%;" |
| | | :disabled="row.returned" |
| | | @change="val => handleActualQtyChange(row, val)" /> |
| | | </template> |
| | | </el-table-column> |
| | | </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="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"> |
| | |
| | | </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> |
| | |
| | | </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, |
| | | updateMaterialPickingLedger, |
| | | } 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.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; |
| | | materialDetailLoading.value = true; |
| | | materialDetailTableData.value = []; |
| | | try { |
| | | const res = await listMaterialPickingDetail(props.orderRow.id); |
| | | materialDetailTableData.value = (res.data || []).map(item => ({ |
| | | ...item, |
| | | actualQty: |
| | | item.actualQty ?? |
| | | Number(item.pickQuantity || 0) + Number(item.feedingQty || 0), |
| | | returnQty: item.returnQty ?? 0, |
| | | })); |
| | | } finally { |
| | | materialDetailLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | watch( |
| | | () => dialogVisible.value, |
| | | visible => { |
| | | if (visible) { |
| | | loadDetailList(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | 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); |
| | | }); |
| | | 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 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> |