| | |
| | | @close="handleClose"> |
| | | <div class="material-toolbar"> |
| | | <el-button type="primary" |
| | | plain |
| | | @click="handleAddPurchase">新增采购</el-button> |
| | | <el-button type="primary" |
| | | @click="handleAddMaterialRow">新增</el-button> |
| | | </div> |
| | | <el-table v-loading="materialTableLoading" |
| | | :data="materialTableData" |
| | | border |
| | | @selection-change="handleMaterialSelectionChange" |
| | | row-key="tempId"> |
| | | <el-table-column align="center" |
| | | type="selection" |
| | | width="55" /> |
| | | <el-table-column align="center" |
| | | type="index" |
| | | label="序号" |
| | | width="60" /> |
| | | <el-table-column label="工序名称" |
| | | min-width="100"> |
| | | <template #default="{ row }"> |
| | |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-indicator |
| | | clearable |
| | | placeholder="请选择批号" |
| | | style="width: 100%;"> |
| | | style="width: 100%;" |
| | | @change="val => handleBatchNoChange(row, val)"> |
| | | <el-option v-for="item in row.batchNoList" |
| | | :key="item" |
| | | :label="item" |
| | | :label="getBatchOptionLabel(row, item)" |
| | | :value="item" /> |
| | | </el-select> |
| | | </template> |
| | |
| | | @change="val => handleRequiredQtyChange(row, val)" /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="库存余量" |
| | | min-width="90"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.stockQty === null">-</span> |
| | | <span v-else>{{ stripTrailingZeros(row.stockQty) }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="已领数量" |
| | | min-width="90"> |
| | | <template #default="{ row }"> |
| | | {{ stripTrailingZeros(row.pickedQty) ?? 0 }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="剩余可领" |
| | | <el-table-column label="剩余需领" |
| | | min-width="90"> |
| | | <template #default="{ row }"> |
| | | <span :class="{ 'remaining-empty': Number(row.remainingQty) <= 0 }"> |
| | |
| | | <ProductSelectDialog v-model="materialProductDialogVisible" |
| | | @confirm="handleMaterialProductConfirm" |
| | | single /> |
| | | <PurchaseLedgerAddDialog ref="purchaseLedgerAddDialogRef" |
| | | v-model="purchaseLedgerDialogVisible" /> |
| | | <!-- request-url="/stockInventory/rawMaterials" --> |
| | | </div> |
| | | </template> |
| | |
| | | import { computed, ref, watch } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue"; |
| | | import PurchaseLedgerAddDialog from "./PurchaseLedgerAddDialog.vue"; |
| | | import { findProductProcessRouteItemList } from "@/api/productionManagement/productProcessRoute.js"; |
| | | import { getStockInventoryByModelId } from "@/api/inventoryManagement/stockInventory.js"; |
| | | import { |
| | | listMaterialPickingDetail, |
| | | listMaterialPickingBom, |
| | |
| | | const materialTableLoading = ref(false); |
| | | const materialSaving = ref(false); |
| | | const materialTableData = ref([]); |
| | | const selectedMaterialRows = ref([]); |
| | | const purchaseLedgerDialogVisible = ref(false); |
| | | const purchaseLedgerAddDialogRef = ref(null); |
| | | |
| | | const isSaveDisabled = computed(() => { |
| | | if (materialTableLoading.value) return true; |
| | |
| | | returnedQty, |
| | | remainingQty, |
| | | pickQty: 0, |
| | | stockQty: null, |
| | | batchNo: [], |
| | | batchNoList, |
| | | batchQtyMap: {}, |
| | | }; |
| | | }; |
| | | |
| | | // 批号选项展示为“批号(库存数量)” |
| | | const getBatchOptionLabel = (row, batchNo) => |
| | | `${batchNo}(${stripTrailingZeros(row.batchQtyMap?.[batchNo] ?? 0)})`; |
| | | |
| | | const getProcessOptions = async () => { |
| | | if (!props.orderRow?.id) return; |
| | |
| | | }) |
| | | ); |
| | | }); |
| | | // 先查出库存余量再入表,保证首次打开即展示(rows 尚未转为响应式代理, |
| | | // 入表后再改原始对象不会触发视图更新) |
| | | await loadStockQtyForRows(rows); |
| | | // 打开领料台账不默认选中批号:只有手动选择了批号的行才会领料 |
| | | materialTableData.value = rows; |
| | | } finally { |
| | | materialTableLoading.value = false; |
| | |
| | | |
| | | const handleClose = () => { |
| | | materialTableData.value = []; |
| | | selectedMaterialRows.value = []; |
| | | currentMaterialSelectRowIndex.value = -1; |
| | | }; |
| | | |
| | | // 按型号汇总所有批次的原料可用库存余量,并记录每个批号对应的库存数量 |
| | | const loadStockQtyForRows = async rows => { |
| | | const modelIds = [ |
| | | ...new Set(rows.map(row => row.materialModelId).filter(Boolean)), |
| | | ]; |
| | | const stockMap = new Map(); |
| | | const batchQtyMap = new Map(); |
| | | await Promise.all( |
| | | modelIds.map(async modelId => { |
| | | try { |
| | | const res = await getStockInventoryByModelId(modelId); |
| | | const list = Array.isArray(res?.data) |
| | | ? res.data |
| | | : res?.data?.records || res?.data?.rows || []; |
| | | const perBatch = {}; |
| | | const total = list.reduce((sum, item) => { |
| | | const qty = toNumber(item?.qualitity ?? item?.quantity); |
| | | if (item?.batchNo) { |
| | | perBatch[item.batchNo] = roundQuantity( |
| | | toNumber(perBatch[item.batchNo]) + qty |
| | | ); |
| | | } |
| | | return sum + qty; |
| | | }, 0); |
| | | stockMap.set(modelId, roundQuantity(total)); |
| | | batchQtyMap.set(modelId, perBatch); |
| | | } catch (e) { |
| | | stockMap.set(modelId, 0); |
| | | batchQtyMap.set(modelId, {}); |
| | | } |
| | | }) |
| | | ); |
| | | rows.forEach(row => { |
| | | if (!row.materialModelId) { |
| | | row.stockQty = null; |
| | | row.batchQtyMap = {}; |
| | | return; |
| | | } |
| | | row.stockQty = stockMap.has(row.materialModelId) |
| | | ? stockMap.get(row.materialModelId) |
| | | : 0; |
| | | row.batchQtyMap = batchQtyMap.get(row.materialModelId) || {}; |
| | | }); |
| | | }; |
| | | |
| | | const handleAddMaterialRow = () => { |
| | | materialTableData.value.push(createMaterialRow()); |
| | | }; |
| | | |
| | | const handleMaterialSelectionChange = selection => { |
| | | selectedMaterialRows.value = selection; |
| | | }; |
| | | |
| | | const handleAddPurchase = () => { |
| | | if (selectedMaterialRows.value.length === 0) { |
| | | ElMessage.warning("请选择需要新增采购的原料行"); |
| | | return; |
| | | } |
| | | const rowsWithModel = selectedMaterialRows.value.filter( |
| | | row => row.materialModelId |
| | | ); |
| | | if (rowsWithModel.length === 0) { |
| | | ElMessage.warning("所选行缺少原料型号,请先选择原料后再新增采购"); |
| | | return; |
| | | } |
| | | const productRows = rowsWithModel.map(row => ({ |
| | | productModelId: row.materialModelId, |
| | | productCategory: row.materialName, |
| | | specificationModel: row.materialModel, |
| | | unit: row.unit, |
| | | quantity: row.demandedQuantity, |
| | | isChecked: false, |
| | | })); |
| | | purchaseLedgerAddDialogRef.value?.open(productRows); |
| | | }; |
| | | |
| | | const handleDeleteMaterialRow = index => { |
| | |
| | | item => item.name === operationName |
| | | ); |
| | | row.technologyOperationId = process?.technologyOperationId; |
| | | }; |
| | | |
| | | // 选择批号即表示本行本次领料:自动带出剩余需领数量;清空批号表示本行不领料 |
| | | const handleBatchNoChange = (row, batchNo) => { |
| | | if (!Array.isArray(batchNo) || batchNo.length === 0) { |
| | | row.pickQty = 0; |
| | | return; |
| | | } |
| | | if (toNumber(row.pickQty) <= 0) { |
| | | row.pickQty = roundQuantity(row.remainingQty); |
| | | } |
| | | }; |
| | | |
| | | const handleRequiredQtyChange = (row, val) => { |
| | |
| | | row.unit = product.unit || product.measureUnit || ""; |
| | | row.batchNoList = Array.isArray(product.batchNoList) ? product.batchNoList : []; |
| | | row.batchNo = []; |
| | | row.pickQty = 0; |
| | | currentMaterialSelectRowIndex.value = -1; |
| | | materialProductDialogVisible.value = false; |
| | | loadStockQtyForRows([row]); |
| | | }; |
| | | |
| | | const validateMaterialRows = () => { |