⚡ 调度剩余数量按本次填写联动,公路运输司机手机号必填

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-21 11:53:56 +08:00
parent c1863d0b6a
commit 8d25e186b1
2 changed files with 122 additions and 28 deletions
@@ -3774,7 +3774,7 @@
</el-table-column>
<el-table-column label="剩余数量" min-width="130" align="center">
<template #default="{ row }">
{{ formatDispatchQuantity(dispatchItemRemainingQuantity(row)) }}
{{ formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(row)) }}
</template>
</el-table-column>
<el-table-column min-width="130" align="center">
@@ -4031,7 +4031,7 @@
</el-input>
<div class="business-crud-page__dispatch-quantity-hint">
剩余数量:{{
formatDispatchQuantity(dispatchItemHintRemainingQuantity(dispatchItemForm))
formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(dispatchItemForm))
}}
{{ dispatchItemForm.quantityUnit || '吨' }}
</div>
@@ -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();
@@ -1677,7 +1677,7 @@
{{
isDispatchPlanQuantityUnlimited(row)
? '-'
: formatDispatchQuantity(dispatchItemRemainingQuantity(row))
: formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(row))
}}
</template>
</el-table-column>
@@ -1861,7 +1861,7 @@
/>
</el-select>
</el-form-item>
<el-form-item label="司机" :required="dispatchItemForm.carrierType !== '承运商'">
<el-form-item label="司机" required>
<el-autocomplete
v-model="dispatchItemForm.driverName"
placeholder="请输入"
@@ -1874,7 +1874,7 @@
</el-form-item>
<el-form-item
:label="dispatchItemForm.carrierType === '承运商' ? '手机号' : '司机手机号'"
:required="dispatchItemForm.carrierType !== '承运商'"
required
>
<el-input v-model="dispatchItemForm.driverPhone" placeholder="请输入" />
</el-form-item>
@@ -1955,7 +1955,7 @@
class="transport-plan-page__dispatch-quantity-hint"
>
剩余数量:{{
formatDispatchQuantity(dispatchItemHintRemainingQuantity(dispatchItemForm))
formatDispatchQuantity(dispatchItemDisplayRemainingQuantity(dispatchItemForm))
}}
{{ dispatchItemForm.quantityUnit || '吨' }}
</div>
@@ -2096,11 +2096,7 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="dispatchIsRoadTransport"
label="司机"
:required="dispatchItemForm.carrierType === '自运'"
>
<el-form-item v-if="dispatchIsRoadTransport" label="司机" required>
<el-autocomplete
v-model="dispatchItemForm.driverName"
placeholder="请输入"
@@ -2111,11 +2107,7 @@
@select="item => handleTaskDriverSelect('dispatch', item)"
/>
</el-form-item>
<el-form-item
v-if="dispatchIsRoadTransport"
label="手机号"
:required="dispatchItemForm.carrierType === '自运'"
>
<el-form-item v-if="dispatchIsRoadTransport" label="手机号" required>
<el-input v-model="dispatchItemForm.driverPhone" placeholder="请输入" />
</el-form-item>
<el-form-item
@@ -3158,6 +3150,8 @@ export default {
dispatchItemIndex: -1,
dispatchItemForm: defaultDispatchRow(),
dispatchItemCargoRows: [],
// 本次数量变更版本号:驱动同表其他行「剩余数量」同步重算
dispatchCargoQuantityVersion: 0,
dispatchItemAttachmentRows: [],
selectedDispatchItemAttachmentRows: [],
attachmentRows: [],
@@ -7708,6 +7702,7 @@ export default {
row.cargoName = '';
row.specification = '';
row.model = '';
this.dispatchCargoQuantityVersion += 1;
},
handleDispatchConfiguredCargoNameChange(row, value) {
const nextValue = String(value ?? row.cargoName ?? '').trim();
@@ -7716,6 +7711,7 @@ export default {
);
if (!option) {
row.cargoName = nextValue;
this.dispatchCargoQuantityVersion += 1;
return;
}
const previousType = String(row.cargoType || '').trim();
@@ -7730,6 +7726,7 @@ export default {
}
row.specification = option.specification || row.specification || '';
row.model = option.model || row.model || '';
this.dispatchCargoQuantityVersion += 1;
},
openDispatchItemDialog(index = -1, row = defaultDispatchRow()) {
const defaultSummaryItem =
@@ -8058,6 +8055,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.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]);