| | |
| | | <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="supplementTime" min-width="180" /> |
| | | <el-table-column label="备注" prop="remark" min-width="200" /> |
| | | <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"> |
| | |
| | | <el-table-column label="退料汇总数量" prop="returnQtyTotal" min-width="140" /> |
| | | </el-table> |
| | | |
| | | <el-card class="approver-card" shadow="never"> |
| | | <template #header> |
| | | <div class="card-header-wrapper"> |
| | | <span class="card-title">审批人选择</span> |
| | | <el-button type="primary" size="small" @click="addApproverNode">新增节点</el-button> |
| | | </div> |
| | | </template> |
| | | <div class="approver-nodes-container"> |
| | | <div v-for="(node, index) in approverNodes" :key="node.id" class="approver-node-item"> |
| | | <div class="approver-node-label"> |
| | | <span class="node-step">{{ index + 1 }}</span> |
| | | <span class="node-text">审批人</span> |
| | | </div> |
| | | <el-select v-model="node.userId" placeholder="选择人员" class="approver-select" clearable> |
| | | <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" /> |
| | | </el-select> |
| | | <el-button v-if="approverNodes.length > 1" type="danger" size="small" @click="removeApproverNode(index)"> |
| | | 删除 |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" :loading="materialReturnConfirming" @click="handleReturnConfirm">确认提交</el-button> |
| | |
| | | import { computed, ref, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import { listMaterialPickingDetail, listMaterialSupplementRecord, confirmMaterialReturn } from "@/api/productionManagement/productionOrder.js"; |
| | | import { userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | |
| | | const props = defineProps({ |
| | | modelValue: { type: Boolean, default: false }, |
| | |
| | | const supplementRecordTableData = ref([]); |
| | | const returnSummaryDialogVisible = ref(false); |
| | | const returnSummaryList = ref([]); |
| | | const userList = ref([]); |
| | | const approverNodes = ref([{ id: Date.now(), userId: undefined }]); |
| | | const calcReturnQty = item => |
| | | Number(item.pickQty || 0) + Number(item.supplementQty || 0) - Number(item.actualQty || 0); |
| | | const canOpenReturnSummary = computed(() => |
| | | materialDetailTableData.value.some(item => Number(item.returnQty || 0) > 0) |
| | | materialDetailTableData.value.some(item => calcReturnQty(item) > 0) |
| | | ); |
| | | |
| | | const loadDetailList = async () => { |
| | |
| | | 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, |
| | |
| | | unit: item.unit || "", |
| | | returnQtyTotal: 0, |
| | | }; |
| | | old.returnQtyTotal += Number(item.returnQty || 0); |
| | | old.returnQtyTotal += returnQty; |
| | | map.set(key, old); |
| | | }); |
| | | return Array.from(map.values()); |
| | | }; |
| | | |
| | | const loadUserList = async () => { |
| | | if (userList.value.length > 0) return; |
| | | const res = await userListNoPageByTenantId(); |
| | | userList.value = res.data || []; |
| | | }; |
| | | |
| | | const openReturnSummaryDialog = async () => { |
| | | if (!canOpenReturnSummary.value) { |
| | | ElMessage.warning("退料数量大于0时才能退料确认"); |
| | | ElMessage.warning("退料数量=领用数量+补料数量-实际数量,且需大于0"); |
| | | return; |
| | | } |
| | | returnSummaryList.value = buildReturnSummary(); |
| | | approverNodes.value = [{ id: Date.now(), userId: undefined }]; |
| | | await loadUserList(); |
| | | returnSummaryDialogVisible.value = true; |
| | | }; |
| | | |
| | | const addApproverNode = () => { |
| | | approverNodes.value.push({ id: Date.now() + Math.random(), userId: undefined }); |
| | | }; |
| | | |
| | | const removeApproverNode = index => { |
| | | approverNodes.value.splice(index, 1); |
| | | }; |
| | | |
| | | const handleReturnConfirm = async () => { |
| | | if (!props.orderRow?.id) return; |
| | | const approverList = approverNodes.value |
| | | .filter(item => item.userId) |
| | | .map((item, index) => ({ userId: item.userId, sort: index + 1 })); |
| | | if (approverList.length === 0) { |
| | | ElMessage.warning("请至少选择一位审批人"); |
| | | return; |
| | | } |
| | | materialReturnConfirming.value = true; |
| | | try { |
| | | await confirmMaterialReturn({ |
| | | orderId: props.orderRow.id, |
| | | returnSummaryList: returnSummaryList.value, |
| | | approverList, |
| | | }); |
| | | returnSummaryDialogVisible.value = false; |
| | | dialogVisible.value = false; |
| | |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .approver-card { |
| | | margin-top: 12px; |
| | | } |
| | | .card-header-wrapper { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | .approver-nodes-container { |
| | | display: flex; |
| | | flex-direction: column; |
| | | gap: 8px; |
| | | } |
| | | .approver-node-item { |
| | | display: flex; |
| | | gap: 8px; |
| | | align-items: center; |
| | | } |
| | | .approver-node-label { |
| | | display: flex; |
| | | gap: 4px; |
| | | min-width: 88px; |
| | | align-items: center; |
| | | } |
| | | .node-step { |
| | | width: 20px; |
| | | height: 20px; |
| | | line-height: 20px; |
| | | text-align: center; |
| | | border-radius: 50%; |
| | | background: #409eff; |
| | | color: #fff; |
| | | font-size: 12px; |
| | | } |
| | | .approver-select { |
| | | flex: 1; |
| | | } |
| | | </style> |
| | | <style scoped lang="scss"></style> |