diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue index 484ead8..eb23da3 100644 --- a/src/views/business/components/business-crud-page.vue +++ b/src/views/business/components/business-crud-page.vue @@ -3774,7 +3774,7 @@ @@ -4031,7 +4031,7 @@
剩余数量:{{ - formatDispatchQuantity(dispatchItemHintRemainingQuantity(dispatchItemForm)) + formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(dispatchItemForm)) }} {{ dispatchItemForm.quantityUnit || '吨' }}
@@ -5740,6 +5740,8 @@ export default { dispatchItemIndex: -1, dispatchItemForm: defaultDispatchRow(), dispatchItemCargoRows: [], + // 本次数量变更版本号:驱动同表其他行「剩余数量」同步重算 + dispatchCargoQuantityVersion: 0, dispatchItemAttachmentRows: [], selectedDispatchItemAttachmentRows: [], attachmentRows: [], @@ -12776,18 +12778,23 @@ export default { row.cargoName = ''; row.specification = ''; row.model = ''; + this.dispatchCargoQuantityVersion += 1; }, handleDispatchConfiguredCargoNameChange(row, value) { const option = this.getDispatchConfiguredCargoNameOptions(row).find( item => String(item.value) === String(value || '') ); row.cargoName = option?.value || ''; - if (!option) return; + if (!option) { + this.dispatchCargoQuantityVersion += 1; + return; + } row.cargoType = option.cargoTypeLabel || row.cargoType || ''; row.cargoTypeCode = option.cargoTypeCode || row.cargoTypeCode || ''; row.cargoTypePath = option.cargoTypePath || row.cargoTypePath || []; row.specification = option.specification || row.specification || ''; row.model = option.model || row.model || ''; + this.dispatchCargoQuantityVersion += 1; }, openDispatchItemDialog(index = -1, row = defaultDispatchRow()) { const defaultSummaryItem = @@ -13119,6 +13126,7 @@ export default { const parts = text.split('.'); row.quantity = parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 3)}` : parts[0]; + this.dispatchCargoQuantityVersion += 1; }, handleDispatchCargoNumberInput(row, prop, value) { this.handleTaskFullFreightNumberInput(row, prop, value); @@ -13138,12 +13146,14 @@ export default { const nextRow = this.normalizeTransportCargoRow({ priceUnit: '元/吨' }); if (index > -1) this.dispatchItemCargoRows.splice(index + 1, 0, nextRow); else this.dispatchItemCargoRows.push(nextRow); + this.dispatchCargoQuantityVersion += 1; }, removeDispatchCargoRow(index) { this.dispatchItemCargoRows.splice(index, 1); if (!this.dispatchItemCargoRows.length) { this.dispatchItemCargoRows.push(this.normalizeTransportCargoRow()); } + this.dispatchCargoQuantityVersion += 1; }, dispatchCargoFreightAmount(cargo = {}) { const quantity = Number(cargo.quantity || 0); @@ -13217,11 +13227,52 @@ export default { }, 0) ); }, - /** 精简录入提示:可调度余量扣减当前本次数量后的剩余 */ - dispatchItemHintRemainingQuantity(row = {}) { + /** + * 当前编辑表单中,与指定行同货物(有身份)或同单位(无身份)已占用的本次数量合计。 + * 用于剩余数量展示联动,不参与提交校验(校验仍按整单合计对比计划余量)。 + */ + getDispatchFormOccupiedQuantity(row = {}) { + const goodsRows = + this.dispatchItemForm?.taskEntryMode === 'full' + ? this.dispatchItemCargoRows || [] + : [this.dispatchItemForm || row]; + const hasCargoIdentity = Boolean( + String(row.cargoName || row.goodsName || row.name || '').trim() || + String( + row.cargoType || row.secondCargoTypeName || row.goodsType || row.type || '' + ).trim() + ); + const unit = this.getDispatchQuantityUnit(row); + const cargoKey = this.getDispatchCargoIdentity(row); + return goodsRows.reduce((sum, goods) => { + if (!goods) return sum; + if (hasCargoIdentity) { + const goodsHasIdentity = Boolean( + String(goods.cargoName || goods.goodsName || goods.name || '').trim() || + String( + goods.cargoType || goods.secondCargoTypeName || goods.goodsType || goods.type || '' + ).trim() + ); + if (!goodsHasIdentity || this.getDispatchCargoIdentity(goods) !== cargoKey) { + return sum; + } + } else if (this.getDispatchQuantityUnit(goods) !== unit) { + return sum; + } + return sum + this.parseDispatchQuantity(goods.quantity); + }, 0); + }, + /** 展示用剩余数量:计划可调度余量扣减本单已填本次数量(含当前行) */ + dispatchItemDisplayRemainingQuantity(row = {}) { + // 依赖版本号,确保一行本次数量变化后其他行剩余数量一并刷新 + void this.dispatchCargoQuantityVersion; const available = this.dispatchItemRemainingQuantity(row); - const current = this.parseDispatchQuantity(row.quantity); - return Math.max(available - current, 0); + const occupied = this.getDispatchFormOccupiedQuantity(row); + return Math.max(available - occupied, 0); + }, + /** 精简录入提示:与完整录入剩余数量展示规则一致 */ + dispatchItemHintRemainingQuantity(row = {}) { + return this.dispatchItemDisplayRemainingQuantity(row); }, pruneDispatchPendingRows(rows = []) { const planQuantities = new Map(); diff --git a/src/views/business/components/transport-plan-page.vue b/src/views/business/components/transport-plan-page.vue index e4af27c..1eccc90 100644 --- a/src/views/business/components/transport-plan-page.vue +++ b/src/views/business/components/transport-plan-page.vue @@ -1677,7 +1677,7 @@ {{ isDispatchPlanQuantityUnlimited(row) ? '-' - : formatDispatchQuantity(dispatchItemRemainingQuantity(row)) + : formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(row)) }}
@@ -1861,7 +1861,7 @@ /> - + @@ -1955,7 +1955,7 @@ class="transport-plan-page__dispatch-quantity-hint" > 剩余数量:{{ - formatDispatchQuantity(dispatchItemHintRemainingQuantity(dispatchItemForm)) + formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(dispatchItemForm)) }} {{ dispatchItemForm.quantityUnit || '吨' }} @@ -2096,11 +2096,7 @@ /> - + - + 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 3)}` : parts[0]; + this.dispatchCargoQuantityVersion += 1; }, handleDispatchCargoNumberInput(row, prop, value) { this.handleFreightNumberInput(row, prop, value); @@ -8077,12 +8075,14 @@ export default { const nextRow = this.normalizeTransportCargoRow({ priceUnit: this.defaultPriceUnit }); if (index > -1) this.dispatchItemCargoRows.splice(index + 1, 0, nextRow); else this.dispatchItemCargoRows.push(nextRow); + this.dispatchCargoQuantityVersion += 1; }, removeDispatchCargoRow(index) { this.dispatchItemCargoRows.splice(index, 1); if (!this.dispatchItemCargoRows.length) { this.dispatchItemCargoRows.push(this.normalizeTransportCargoRow()); } + this.dispatchCargoQuantityVersion += 1; }, dispatchCargoFreightAmount(cargo = {}) { if (cargo.freightAmount !== undefined && cargo.freightAmount !== null) { @@ -8232,11 +8232,50 @@ export default { }, 0) ); }, - /** 精简录入提示:可调度余量扣减当前本次数量后的剩余 */ - dispatchItemHintRemainingQuantity(row = {}) { + /** + * 当前编辑表单中,与指定行同货物(有身份)或同单位(无身份)已占用的本次数量合计。 + * 用于剩余数量展示联动,不参与提交校验(校验仍按整单合计对比计划余量)。 + */ + getDispatchFormOccupiedQuantity(row = {}) { + const goodsRows = + this.dispatchItemForm?.taskEntryMode === 'full' + ? this.dispatchItemCargoRows || [] + : [this.dispatchItemForm || row]; + const hasCargoIdentity = Boolean( + String(row.cargoName || row.goodsName || row.name || '').trim() || + String(row.cargoType || row.secondCargoTypeName || row.goodsType || row.type || '').trim() + ); + const unit = this.getDispatchQuantityUnit(row); + const cargoKey = this.getDispatchCargoIdentity(row); + return goodsRows.reduce((sum, goods) => { + if (!goods) return sum; + if (hasCargoIdentity) { + const goodsHasIdentity = Boolean( + String(goods.cargoName || goods.goodsName || goods.name || '').trim() || + String( + goods.cargoType || goods.secondCargoTypeName || goods.goodsType || goods.type || '' + ).trim() + ); + if (!goodsHasIdentity || this.getDispatchCargoIdentity(goods) !== cargoKey) { + return sum; + } + } else if (this.getDispatchQuantityUnit(goods) !== unit) { + return sum; + } + return sum + this.parseDispatchQuantity(goods.quantity); + }, 0); + }, + /** 展示用剩余数量:计划可调度余量扣减本单已填本次数量(含当前行) */ + dispatchItemDisplayRemainingQuantity(row = {}) { + // 依赖版本号,确保一行本次数量变化后其他行剩余数量一并刷新 + void this.dispatchCargoQuantityVersion; const available = this.dispatchItemRemainingQuantity(row); - const current = this.parseDispatchQuantity(row.quantity); - return Math.max(available - current, 0); + const occupied = this.getDispatchFormOccupiedQuantity(row); + return Math.max(available - occupied, 0); + }, + /** 精简录入提示:与完整录入剩余数量展示规则一致 */ + dispatchItemHintRemainingQuantity(row = {}) { + return this.dispatchItemDisplayRemainingQuantity(row); }, pruneDispatchPendingRows(rows = []) { const planQuantities = new Map(); @@ -8334,13 +8373,17 @@ export default { const fullEntry = row.taskEntryMode === 'full'; const roadTransport = this.resolveTransportMode(row.transportType) === 'road'; if (fullEntry) { - if (roadTransport && row.carrierType === '自运') { + // 公路运输时司机/手机号一律必填(含承运类型为承运商) + if (roadTransport && row.carrierType) { requiredFields.push(['driverName', '司机']); requiredFields.push(['driverPhone', '手机号']); } - } else if (row.carrierType && row.carrierType !== '承运商') { + } else if (row.carrierType) { requiredFields.push(['driverName', '司机']); - requiredFields.push(['driverPhone', '司机手机号']); + requiredFields.push([ + 'driverPhone', + row.carrierType === '承运商' ? '手机号' : '司机手机号', + ]); } const emptyField = requiredFields.find(([prop]) => this.isBillingFieldEmpty(row[prop])); if (emptyField) return requiredMessage(emptyField[1]);