| src/api/productionManagement/productionOrder.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/inventoryManagement/stockManagement/New.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/productionOrder/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/productionManagement/productionOrder.js
@@ -72,23 +72,47 @@ } // ç产订å-ä¿å颿å°è´¦ // export function saveMaterialPickingLedger(data) { // return request({ // url: "/productOrderMaterial/save", // method: "post", // data, // }); // } export function saveMaterialPickingLedger(data) { return request({ url: "/productOrderMaterial/save", url: "/productionOrderPick/savePick", method: "post", data, }); } // ç产订å-é¢æè¯¦æ å表 export function listMaterialPickingDetail(query) { export function updateMaterialPickingLedger(data) { return request({ url: "/productOrderMaterial/detailList", method: "get", params: query, url: "/productionOrderPick/updatePick", method: "post", data, }); } // ç产订å-é¢æè¯¦æ å表 // export function listMaterialPickingDetail(query) { // return request({ // url: "/productOrderMaterial/detailList", // method: "get", // params: query, // }); // } export function listMaterialPickingBom(productionOrderId) { return request({ url: "/productionOrder/pick/" + productionOrderId, method: "get", }); } export function listMaterialPickingDetail(productionOrderId) { return request({ url: "/productionOrderPick/detail/" + productionOrderId, method: "get", }); } // ç产订å-è¡¥æè®°å½å表 export function listMaterialSupplementRecord(query) { return request({ src/views/inventoryManagement/stockManagement/New.vue
@@ -1,14 +1,14 @@ <template> <div> <el-dialog v-model="isShow" <el-dialog v-model="isShow" title="æ°å¢åºå" width="800" @close="closeModal" > <el-form label-width="140px" :model="formState" label-position="top" ref="formRef"> <el-form-item label="产ååç§°" @close="closeModal"> <el-form label-width="140px" :model="formState" label-position="top" ref="formRef"> <el-form-item label="产ååç§°" prop="productModelId" :rules="[ { @@ -16,29 +16,23 @@ message: 'è¯·éæ©äº§å', trigger: 'change', } ]" > <el-button type="primary" @click="showProductSelectDialog = true"> ]"> <el-button type="primary" @click="showProductSelectDialog = true"> {{ formState.productName ? formState.productName : 'éæ©äº§å' }} </el-button> </el-form-item> <el-form-item label="è§æ ¼" prop="productModelName" > <el-input v-model="formState.productModelName" disabled /> <el-form-item label="è§æ ¼" prop="productModelName"> <el-input v-model="formState.productModelName" disabled /> </el-form-item> <el-form-item label="åä½" prop="unit" > <el-input v-model="formState.unit" disabled /> <el-form-item label="åä½" prop="unit"> <el-input v-model="formState.unit" disabled /> </el-form-item> <el-form-item label="åºåç±»å" <el-form-item label="åºåç±»å" prop="type" :rules="[ { @@ -46,51 +40,58 @@ message: 'è¯·éæ©åºåç±»å', trigger: 'change', } ]" > <el-select v-model="formState.type" placeholder="è¯·éæ©åºåç±»å"> <el-option label="åæ ¼åºå" value="qualified" /> <el-option label="ä¸åæ ¼åºå" value="unqualified" /> ]"> <el-select v-model="formState.type" placeholder="è¯·éæ©åºåç±»å"> <el-option label="åæ ¼åºå" value="qualified" /> <el-option label="ä¸åæ ¼åºå" value="unqualified" /> </el-select> </el-form-item> <el-form-item label="åºåæ°é" prop="qualitity" > <el-input-number v-model="formState.qualitity" :step="1" :min="1" style="width: 100%" /> <el-form-item label="åºåæ°é" prop="qualitity"> <el-input-number v-model="formState.qualitity" :step="1" :min="1" style="width: 100%" /> </el-form-item> <el-form-item label="æ¹å·" <el-form-item label="æ¹å·" prop="batchNo" > <el-input v-model="formState.batchNo" placeholder="请è¾å ¥æ¹å·" /> :rules="[ { required: true, message: '请è¾å ¥æ¹å·', trigger: 'blur', } ]"> <el-input v-model="formState.batchNo" placeholder="请è¾å ¥æ¹å·" /> </el-form-item> <el-form-item v-if="formState.type === 'qualified'" <el-form-item v-if="formState.type === 'qualified'" label="åºåé¢è¦æ°é" prop="warnNum" > <el-input-number v-model="formState.warnNum" :step="1" :min="0" :max="formState.qualitity" style="width: 100%" /> prop="warnNum"> <el-input-number v-model="formState.warnNum" :step="1" :min="0" :max="formState.qualitity" style="width: 100%" /> </el-form-item> <el-form-item label="夿³¨" prop="remark"> <el-input v-model="formState.remark" type="textarea" /> <el-form-item label="夿³¨" prop="remark"> <el-input v-model="formState.remark" type="textarea" /> </el-form-item> </el-form> <!-- 产åéæ©å¼¹çª --> <ProductSelectDialog v-model="showProductSelectDialog" <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" :top-product-parent-id="props.topProductParentId" single /> single /> <template #footer> <div class="dialog-footer"> <el-button type="primary" @click="handleSubmit">确认</el-button> <el-button type="primary" @click="handleSubmit">确认</el-button> <el-button @click="closeModal">åæ¶</el-button> </div> </template> @@ -113,10 +114,10 @@ type: Number, default: undefined, required: false, } }, }); const emit = defineEmits(['update:visible', 'completed']); const emit = defineEmits(["update:visible", "completed"]); // ååºå¼æ°æ®ï¼æ¿ä»£é项å¼ç dataï¼ const formState = ref({ @@ -129,7 +130,7 @@ qualitity: 0, batchNo: null, warnNum: 0, remark: '', remark: "", }); const isShow = computed({ @@ -137,20 +138,23 @@ return props.visible; }, set(val) { emit('update:visible', val); emit("update:visible", val); }, }); const showProductSelectDialog = ref(false); // æ¹å·ä¸ºç©ºæ¶è½¬ä¸º null watch(() => formState.value.batchNo, (val) => { if (val === '') { watch( () => formState.value.batchNo, val => { if (val === "") { formState.value.batchNo = null; } }); } ); let { proxy } = getCurrentInstance() let { proxy } = getCurrentInstance(); const closeModal = () => { // éç½®è¡¨åæ°æ® @@ -164,13 +168,13 @@ qualitity: 0, batchNo: null, warnNum: 0, remark: '', remark: "", }; isShow.value = false; }; // 产åéæ©å¤ç const handleProductSelect = async (products) => { const handleProductSelect = async products => { if (products && products.length > 0) { const product = products[0]; formState.value.productId = product.productId; @@ -180,7 +184,7 @@ formState.value.unit = product.unit; showProductSelectDialog.value = false; // 触å表åéªè¯æ´æ° proxy.$refs["formRef"]?.validateField('productModelId'); proxy.$refs["formRef"]?.validateField("productModelId"); } }; @@ -196,29 +200,27 @@ proxy.$modal.msgError("è¯·éæ©è§æ ¼"); return; } if (formState.value.type === 'qualified') { if (formState.value.type === "qualified") { addStockInRecordOnly(formState.value).then(res => { // å ³éæ¨¡ææ¡ isShow.value = false; // åç¥ç¶ç»ä»¶å·²å®æ emit('completed'); emit("completed"); proxy.$modal.msgSuccess("æäº¤æå"); }) }); } else { formState.value.warnNum = 0; createStockUnInventory(formState.value).then(res => { // å ³éæ¨¡ææ¡ isShow.value = false; // åç¥ç¶ç»ä»¶å·²å®æ emit('completed'); emit("completed"); proxy.$modal.msgSuccess("æäº¤æå"); }) }); } } }) }); }; defineExpose({ closeModal, 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" <el-button type="warning" :loading="materialReturnConfirming" :disabled="!canOpenReturnSummary" @click="openReturnSummaryDialog" > @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> @@ -68,7 +118,11 @@ <script setup> import { computed, ref, watch } from "vue"; import { ElMessage } from "element-plus"; import { listMaterialPickingDetail, listMaterialSupplementRecord, confirmMaterialReturn } from "@/api/productionManagement/productionOrder.js"; import { listMaterialPickingDetail, listMaterialSupplementRecord, confirmMaterialReturn, } from "@/api/productionManagement/productionOrder.js"; const props = defineProps({ modelValue: { type: Boolean, default: false }, @@ -90,7 +144,9 @@ const returnSummaryDialogVisible = ref(false); const returnSummaryList = ref([]); const calcReturnQty = item => Number(item.pickQty || 0) + Number(item.supplementQty || 0) - Number(item.actualQty || 0); Number(item.pickQuantity || 0) + Number(item.supplementQty || 0) - Number(item.actualQty || 0); const canOpenReturnSummary = computed(() => materialDetailTableData.value.some(item => calcReturnQty(item) > 0) ); @@ -100,7 +156,7 @@ materialDetailLoading.value = true; materialDetailTableData.value = []; try { const res = await listMaterialPickingDetail({ orderId: props.orderRow.id }); const res = await listMaterialPickingDetail(props.orderRow.id); materialDetailTableData.value = res.data || []; } finally { materialDetailLoading.value = false; @@ -126,7 +182,9 @@ supplementRecordLoading.value = true; supplementRecordTableData.value = []; try { const res = await listMaterialSupplementRecord({ materialDetailId: row.id }); const res = await listMaterialSupplementRecord({ materialDetailId: row.id, }); supplementRecordTableData.value = res.data || []; } finally { supplementRecordLoading.value = false; @@ -138,11 +196,13 @@ materialDetailTableData.value.forEach(item => { const returnQty = calcReturnQty(item); if (returnQty <= 0) return; const key = `${item.materialModelId || ""}_${item.materialName || ""}_${item.materialModel || ""}_${item.unit || ""}`; const key = `${item.productModelId || ""}_${item.productName || ""}_${ item.model || "" }_${item.unit || ""}`; const old = map.get(key) || { summaryKey: key, materialName: item.materialName || "", materialModel: item.materialModel || "", materialName: item.productName || "", materialModel: item.model || "", unit: item.unit || "", returnQtyTotal: 0, }; src/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue
@@ -13,25 +13,25 @@ border row-key="tempId"> <el-table-column label="å·¥åºåç§°" min-width="180"> min-width="140"> <template #default="{ row }"> <span v-if="row.bom === true">{{ row.processName || "-" }}</span> <span v-if="row.bom === true">{{ row.operationName || "-" }}</span> <el-select v-else v-model="row.processName" v-model="row.operationName" placeholder="è¯·éæ©å·¥åº" clearable filterable style="width: 100%;" @change="val => handleProcessNameChange(row, val)"> <el-option v-for="item in processOptions" :key="item.id" :key="item.technologyOperationId" :label="item.name" :value="item.name" /> </el-select> </template> </el-table-column> <el-table-column label="åæåç§°" min-width="160"> min-width="140"> <template #default="{ row }"> <span v-if="row.bom === true">{{ row.materialName || "-" }}</span> <el-button v-else @@ -43,17 +43,37 @@ </template> </el-table-column> <el-table-column label="åæåå·" min-width="180"> min-width="140"> <template #default="{ row }"> {{ row.materialModel || "-" }} </template> </el-table-column> <!-- æ¹å·å¤é --> <el-table-column min-width="200"> <template #header> <span style="color: #f56c6c; margin-right: 4px;">*</span> <span>æ¹å·</span> </template> <template #default="{ row }"> <el-select v-model="row.batchNo" multiple collapse-tags collapse-tags-indicator placeholder="è¯·éæ©æ¹å·" style="width: 100%;"> <el-option v-for="item in row.batchNoList" :key="item" :label="item" :value="item" /> </el-select> </template> </el-table-column> <el-table-column label="éæ±æ°é" min-width="120"> <template #default="{ row }"> <span v-if="row.bom === true">{{ row.requiredQty ?? "-" }}</span> <span v-if="row.bom === true">{{ row.demandedQuantity ?? "-" }}</span> <el-input-number v-else v-model="row.requiredQty" v-model="row.demandedQuantity" :min="0" :precision="3" :step="1" @@ -63,7 +83,7 @@ </template> </el-table-column> <el-table-column label="计éåä½" width="120"> width="100"> <template #default="{ row }"> {{ row.unit || "-" }} </template> @@ -110,12 +130,18 @@ import { computed, ref, watch } from "vue"; import { ElMessage } from "element-plus"; import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js"; import { findProductProcessRouteItemList, listMain, } from "@/api/productionManagement/productProcessRoute.js"; import { listMaterialPickingDetail, listMaterialPickingBom, listMaterialPickingLedger, saveMaterialPickingLedger, updateMaterialPickingLedger, } from "@/api/productionManagement/productionOrder.js"; import { queryList2 } from "@/api/productionManagement/productStructure.js"; const props = defineProps({ modelValue: { type: Boolean, default: false }, @@ -139,16 +165,22 @@ const createMaterialRow = (row = {}) => ({ tempId: row.id || `temp_${++materialTempId}`, id: row.id, processId: row.processId, productProcessId: row.productProcessId || row.processId, processName: row.processName || "", processId: row.processId || row.technologyOperationId, technologyOperationId: row.technologyOperationId || row.processId, operationName: row.operationName || "", bom: row.bom === true, materialModelId: row.materialModelId, materialName: row.materialName || "", materialModel: row.materialModel || "", requiredQty: Number(row.requiredQty ?? 0), materialModelId: row.materialModelId || row.productModelId, materialName: row.materialName || row.productName || "", materialModel: row.materialModel || row.model || "", demandedQuantity: Number(row.requiredQty ?? row.demandedQuantity ?? 0), unit: row.unit || "", pickQty: Number(row.pickQty ?? row.requiredQty ?? 0), pickQty: Number(row.pickQty ?? row.pickQuantity ?? 0), batchNo: row.batchNo ? typeof row.batchNo === "string" ? row.batchNo.split(",") : row.batchNo : [], batchNoList: row.batchNoList || [], }); const getProcessOptions = async () => { @@ -161,19 +193,20 @@ : res?.data?.records || []; const processMap = new Map(); routeList.forEach(item => { const processId = item.processId; const processName = item.processName; if (!processId || !processName) return; const key = `${processId}_${processName}`; const processId = item.technologyOperationId; const operationName = item.operationName; if (!processId || !operationName) return; const key = `${processId}_${operationName}`; if (!processMap.has(key)) { processMap.set(key, { id: processId, name: processName, name: operationName, }); } }); processOptions.value = Array.from(processMap.values()); }; const isDetail = ref(true); const loadMaterialData = async () => { if (!props.orderRow?.id) return; @@ -181,23 +214,23 @@ materialTableData.value = []; await getProcessOptions(); try { const detailRes = await listMaterialPickingDetail({ orderId: props.orderRow.id, }); const detailRes = await listMaterialPickingDetail(props.orderRow.id); const detailList = Array.isArray(detailRes?.data) ? detailRes.data : detailRes?.data?.records || []; if (detailList.length > 0) { isDetail.value = true; materialTableData.value = detailList.map(item => createMaterialRow(item)); return; } else { isDetail.value = false; const bomRes = await listMaterialPickingBom(props.orderRow.id); const bomList = Array.isArray(bomRes?.data) ? bomRes.data : bomRes?.data?.records || []; materialTableData.value = bomList.map(item => createMaterialRow(item)); return; } const ledgerRes = await listMaterialPickingLedger({ orderId: props.orderRow.id, }); const ledgerList = Array.isArray(ledgerRes?.data) ? ledgerRes.data : ledgerRes?.data?.records || []; materialTableData.value = ledgerList.map(item => createMaterialRow(item)); } finally { materialTableLoading.value = false; } @@ -225,14 +258,16 @@ materialTableData.value.splice(index, 1); }; const handleProcessNameChange = (row, processName) => { const process = processOptions.value.find(item => item.name === processName); row.productProcessId = process?.id; const handleProcessNameChange = (row, operationName) => { const process = processOptions.value.find( item => item.name === operationName ); row.technologyOperationId = process?.technologyOperationId; }; const handleRequiredQtyChange = (row, val) => { const required = Number(val ?? 0); row.requiredQty = required; row.demandedQuantity = required; if (!row.pickQty || Number(row.pickQty) === 0) { row.pickQty = required; } @@ -246,6 +281,8 @@ }; const handleMaterialProductConfirm = products => { console.log(products, "products"); if (!products || products.length === 0) return; const index = currentMaterialSelectRowIndex.value; if (index < 0 || !materialTableData.value[index]) return; @@ -257,6 +294,7 @@ product.materialName || product.productName || product.name || ""; row.materialModel = product.materialModel || product.model || ""; row.unit = product.unit || product.measureUnit || ""; row.batchNoList = product.batchNoList; currentMaterialSelectRowIndex.value = -1; materialProductDialogVisible.value = false; }; @@ -266,22 +304,24 @@ return { valid: false, message: "è¯·å æ°å¢é¢ææ°æ®" }; } const invalidNewRow = materialTableData.value.find( item => item.bom !== true && (!item.processName || !item.materialName) item => item.bom !== true && (!item.operationName || !item.materialName) ); if (invalidNewRow) { return { valid: false, message: "æ°å¢è¡çå·¥åºåç§°ååæåç§°ä¸ºå¿ å¡«é¡¹" }; } const invalidRow = materialTableData.value.find( item => !item.processName || !item.operationName || !item.materialName || item.requiredQty === null || item.requiredQty === undefined || !item.batchNo || item.batchNo.length === 0 || item.demandedQuantity === null || item.demandedQuantity === undefined || item.pickQty === null || item.pickQty === undefined ); if (invalidRow) { return { valid: false, message: "请å®åå·¥åºãåæåæ°éååä¿å" }; return { valid: false, message: "请å®åå·¥åºãåæãæ¹å·åæ°éååä¿å" }; } return { valid: true, message: "" }; }; @@ -295,22 +335,49 @@ } materialSaving.value = true; try { await saveMaterialPickingLedger({ orderId: props.orderRow.id, items: materialTableData.value.map(item => ({ if (isDetail.value) { await updateMaterialPickingLedger({ productionOrderId: props.orderRow.id, productionOrderPickDto: materialTableData.value.map(item => ({ id: item.id, processId: item.processName, productProcessId: item.productProcessId, processName: item.processName, // processId: item.operationName, technologyOperationId: item.technologyOperationId, operationName: item.operationName, bom: item.bom === true, materialModelId: item.materialModelId, materialName: item.materialName, materialModel: item.materialModel, requiredQty: item.requiredQty, productModelId: item.materialModelId, // materialName: item.materialName, // materialModel: item.materialModel, demandedQuantity: item.demandedQuantity, unit: item.unit, pickQty: item.pickQty, pickQuantity: item.pickQty, batchNo: Array.isArray(item.batchNo) ? item.batchNo.join(",") : item.batchNo, })), }); } else { await saveMaterialPickingLedger({ productionOrderId: props.orderRow.id, productionOrderPickDto: materialTableData.value.map(item => ({ id: item.id, // processId: item.operationName, technologyOperationId: item.technologyOperationId, operationName: item.operationName, bom: item.bom === true, productModelId: item.materialModelId, // materialName: item.materialName, // materialModel: item.materialModel, demandedQuantity: item.demandedQuantity, unit: item.unit, pickQuantity: item.pickQty, batchNo: Array.isArray(item.batchNo) ? item.batchNo.join(",") : item.batchNo, })), }); } ElMessage({ message: "颿æå", type: "success" }); emit("saved"); dialogVisible.value = false; } finally { src/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,159 @@ <template> <el-dialog v-model="dialogVisible" title="è¡¥æ" width="1200px" @close="handleClose"> <el-table v-loading="loading" :data="tableData" border row-key="id"> <el-table-column label="å·¥åºåç§°" prop="operationName" min-width="140" /> <el-table-column label="åæåç§°" prop="productName" min-width="140" /> <el-table-column label="åæåå·" prop="model" min-width="140" /> <el-table-column label="计éåä½" prop="unit" width="100" /> <el-table-column label="é¢ç¨æ°é" prop="pickQuantity" width="100" /> <el-table-column label="è¡¥ææ°é" min-width="150"> <template #default="{ row }"> <el-input-number v-model="row.newSupplementQty" :min="0" :precision="3" :step="1" controls-position="right" placeholder="è¾å ¥è¡¥ææ°é" style="width: 100%;" /> </template> </el-table-column> <el-table-column label="è¡¥æåå " min-width="200"> <template #default="{ row }"> <el-input v-model="row.newSupplementReason" placeholder="è¾å ¥è¡¥æåå " maxlength="200" show-word-limit /> </template> </el-table-column> </el-table> <template #footer> <span class="dialog-footer"> <el-button type="primary" :loading="submitting" @click="handleSubmit">ç¡® å®</el-button> <el-button @click="dialogVisible = false">å æ¶</el-button> </span> </template> </el-dialog> </template> <script setup> import { computed, ref, watch } from "vue"; import { ElMessage } from "element-plus"; import { listMaterialPickingDetail, updateMaterialPickingLedger, } from "@/api/productionManagement/productionOrder.js"; const props = defineProps({ modelValue: { type: Boolean, default: false }, orderRow: { type: Object, default: null }, }); const emit = defineEmits(["update:modelValue", "saved"]); const dialogVisible = computed({ get: () => props.modelValue, set: val => emit("update:modelValue", val), }); const loading = ref(false); const submitting = ref(false); const tableData = ref([]); const loadData = async () => { if (!props.orderRow?.id) return; loading.value = true; try { const res = await listMaterialPickingDetail(props.orderRow.id); tableData.value = (res.data || []).map(item => ({ ...item, newSupplementQty: 0, newSupplementReason: "", })); } catch (e) { console.error("è·åç©ææç»å¤±è´¥ï¼", e); ElMessage.error("è·åç©ææç»å¤±è´¥"); } finally { loading.value = false; } }; watch( () => dialogVisible.value, visible => { if (visible) { loadData(); } } ); const handleClose = () => { tableData.value = []; }; const handleSubmit = async () => { const supplementList = tableData.value.filter( item => item.newSupplementQty > 0 ); if (supplementList.length === 0) { ElMessage.warning("请è³å°è¾å ¥ä¸æ¡è¡¥ææ°é"); return; } const invalidRow = supplementList.find(item => !item.newSupplementReason); if (invalidRow) { ElMessage.warning("请è¾å ¥è¡¥æåå "); return; } submitting.value = true; try { await updateMaterialPickingLedger({ productionOrderId: props.orderRow.id, productionOrderPickDto: tableData.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, feedingQuantity: item.newSupplementQty || 0, feedingReason: item.newSupplementReason || "", pickType: 2, })), }); ElMessage.success("è¡¥ææå"); dialogVisible.value = false; emit("saved"); } catch (e) { console.error("è¡¥æå¤±è´¥ï¼", e); ElMessage.error("è¡¥æå¤±è´¥"); } finally { submitting.value = false; } }; </script> <style scoped lang="scss"> </style> src/views/productionManagement/productionOrder/index.vue
@@ -304,7 +304,7 @@ label: "æä½", align: "center", fixed: "right", width: 340, width: 360, operation: [ { name: "å·¥èºè·¯çº¿", @@ -337,27 +337,20 @@ showSourceData(row); }, }, // { // name: "产åç»æ", // type: "text", // clickFun: row => { // showProductStructure(row); // }, // }, // { // name: "颿", // type: "text", // clickFun: row => { // openMaterialDialog(row); // }, // }, // { // name: "é¢æè¯¦æ ", // type: "text", // clickFun: row => { // openMaterialDetailDialog(row); // }, // }, { name: "颿", type: "text", clickFun: row => { openMaterialDialog(row); }, }, { name: "é¢æè¯¦æ ", type: "text", clickFun: row => { openMaterialDetailDialog(row); }, }, ], }, ]); src/views/productionManagement/workOrderManagement/components/MaterialDialog.vue
@@ -1,86 +1,119 @@ <template> <div> <el-dialog v-model="dialogVisible" <el-dialog v-model="dialogVisible" title="ç©æ" width="1200px" @close="handleCloseMaterialDialog" > <el-table v-loading="materialTableLoading" :data="materialTableData" border row-key="id"> <el-table-column label="å·¥åºåç§°" prop="processName" min-width="140" /> <el-table-column label="åæåç§°" prop="materialName" min-width="140" /> <el-table-column label="åæåå·" prop="materialModel" min-width="140" /> <el-table-column label="计éåä½" prop="unit" min-width="100" /> <el-table-column label="çº¿è¾¹ä»æ°é" prop="pickQty" min-width="100" /> <el-table-column label="è¡¥ææ°é" prop="supplementQty" min-width="100" /> <el-table-column label="å®é æ°é" min-width="140"> @close="handleCloseMaterialDialog"> <el-table v-loading="materialTableLoading" :data="materialTableData" border row-key="id"> <el-table-column label="å·¥åºåç§°" prop="processName" min-width="140" /> <el-table-column label="åæåç§°" prop="materialName" min-width="140" /> <el-table-column label="åæåå·" prop="materialModel" min-width="140" /> <el-table-column label="计éåä½" prop="unit" min-width="100" /> <el-table-column label="çº¿è¾¹ä»æ°é" prop="pickQty" min-width="100" /> <el-table-column label="è¡¥ææ°é" prop="supplementQty" min-width="100" /> <el-table-column label="å®é æ°é" min-width="140"> <template #default="{ row }"> <el-input-number v-model="row.actualQty" <el-input-number v-model="row.actualQty" :min="0" :precision="3" :step="1" controls-position="right" style="width: 100%;" /> style="width: 100%;" /> </template> </el-table-column> <el-table-column label="æä½" align="center" fixed="right" width="180"> <el-table-column label="æä½" align="center" fixed="right" width="180"> <template #default="{ row }"> <el-button type="primary" link @click="openSupplementDialog(row)">è¡¥æ</el-button> <el-button type="info" link @click="openSupplementRecordDialog(row)">è¡¥æè®°å½</el-button> <el-button type="primary" link @click="openSupplementDialog(row)">è¡¥æ</el-button> <el-button type="info" link @click="openSupplementRecordDialog(row)">è¡¥æè®°å½</el-button> </template> </el-table-column> </el-table> <template #footer> <span class="dialog-footer"> <el-button type="primary" :loading="pickSubmitting" @click="handleSubmitPick">é¢ç¨</el-button> <el-button type="primary" :loading="pickSubmitting" @click="handleSubmitPick">é¢ç¨</el-button> <el-button @click="dialogVisible = false">åæ¶</el-button> </span> </template> </el-dialog> <FormDialog v-model="supplementDialogVisible" <FormDialog v-model="supplementDialogVisible" title="è¡¥æ" width="500px" @confirm="handleSubmitSupplement" > <el-form ref="supplementFormRef" :model="supplementForm" :rules="supplementRules" label-width="100px"> <el-form-item label="è¡¥ææ°é" prop="supplementQty"> <el-input-number v-model="supplementForm.supplementQty" @confirm="handleSubmitSupplement"> <el-form ref="supplementFormRef" :model="supplementForm" :rules="supplementRules" label-width="100px"> <el-form-item label="è¡¥ææ°é" prop="supplementQty"> <el-input-number v-model="supplementForm.supplementQty" :min="0.001" :precision="3" :step="1" style="width: 100%;" /> style="width: 100%;" /> </el-form-item> <el-form-item label="è¡¥æåå " prop="supplementReason"> <el-input v-model="supplementForm.supplementReason" <el-form-item label="è¡¥æåå " prop="supplementReason"> <el-input v-model="supplementForm.supplementReason" type="textarea" :rows="3" maxlength="200" show-word-limit placeholder="请è¾å ¥è¡¥æåå " /> placeholder="请è¾å ¥è¡¥æåå " /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button type="primary" :loading="supplementSubmitting" @click="handleSubmitSupplement">ç¡®å®</el-button> <el-button type="primary" :loading="supplementSubmitting" @click="handleSubmitSupplement">ç¡®å®</el-button> <el-button @click="supplementDialogVisible = false">åæ¶</el-button> </span> </template> </FormDialog> <el-dialog v-model="supplementRecordDialogVisible" title="è¡¥æè®°å½" width="900px"> <el-table v-loading="supplementRecordLoading" :data="supplementRecordTableData" border row-key="id"> <el-table-column label="è¡¥ææ°é" prop="supplementQty" min-width="100" /> <el-table-column label="è¡¥æåå " prop="supplementReason" min-width="200" /> <el-table-column label="è¡¥æäºº" prop="supplementUserName" min-width="120" /> <el-table-column label="è¡¥ææ¥æ" prop="supplementTime" min-width="160" /> <el-dialog v-model="supplementRecordDialogVisible" title="è¡¥æè®°å½" width="900px"> <el-table v-loading="supplementRecordLoading" :data="supplementRecordTableData" border row-key="id"> <el-table-column label="è¡¥ææ°é" prop="supplementQty" min-width="100" /> <el-table-column label="è¡¥æåå " prop="supplementReason" min-width="200" /> <el-table-column label="è¡¥æäºº" prop="supplementUserName" min-width="120" /> <el-table-column label="è¡¥ææ¥æ" prop="supplementTime" min-width="160" /> </el-table> <template #footer> <span class="dialog-footer"> @@ -139,8 +172,12 @@ const supplementRecordTableData = ref([]); const supplementRules = { supplementQty: [{ required: true, message: "请è¾å ¥è¡¥ææ°é", trigger: "blur" }], supplementReason: [{ required: true, message: "请è¾å ¥è¡¥æåå ", trigger: "blur" }], supplementQty: [ { required: true, message: "请è¾å ¥è¡¥ææ°é", trigger: "blur" }, ], supplementReason: [ { required: true, message: "请è¾å ¥è¡¥æåå ", trigger: "blur" }, ], }; const loadMaterialTable = async row => { if (!row?.id) return; @@ -235,7 +272,12 @@ if (materialTableData.value.length === 0) { return { valid: false, message: "ææ å¯é¢ç¨ç©æ" }; } const invalidRow = materialTableData.value.find(item => item.actualQty === null || item.actualQty === undefined || item.actualQty === ""); const invalidRow = materialTableData.value.find( item => item.actualQty === null || item.actualQty === undefined || item.actualQty === "" ); if (invalidRow) { return { valid: false, message: "请填åå®é æ°éååé¢ç¨" }; }