✨ 完善配载确认、承运商组织过滤司机与地址展示

配载草稿确认生成正式单并优化详情/列表地址市区展示;运单调度等按承运商联系人所属组织筛选司机车辆;统一地图搜索结果与登录默认账号清理。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 18:35:27 +08:00
co-authored by Cursor
parent 694fe1a433
commit a919781d4f
19 changed files with 1701 additions and 309 deletions
@@ -637,13 +637,14 @@
<span>|</span>
{{ detailRow.planNo || '-' }}
</h2>
<span class="detail-status-text is-dispatching">
<span class="detail-status-text" :class="detailStatusTextClass">
{{ displayStatus(detailRow, 'businessStatus') }}
</span>
<span class="detail-status-text detail-transport-type">
{{
detailRow.transportTypeName ||
formatTransportPlanDetailTransportType(detailRow.transportType)
formatTransportPlanDetailTransportType(detailRow.transportType) ||
'-'
}}
</span>
</div>
@@ -1231,7 +1232,7 @@
{{ dispatchDialogStatusText }}
</el-tag>
<el-tag type="primary" effect="light">
{{ formatDispatchTransportType(dispatchRow.transportType) || '公路运输' }}
{{ formatDispatchTransportType(dispatchRow.transportType) || '-' }}
</el-tag>
</div>
</div>
@@ -1931,7 +1932,7 @@
class="transport-plan-page__dispatch-quantity-hint"
>
剩余数量:{{
formatDispatchQuantity(dispatchItemRemainingQuantity(dispatchItemForm))
formatDispatchQuantity(dispatchItemHintRemainingQuantity(dispatchItemForm))
}}
{{ dispatchItemForm.quantityUnit || '吨' }}
</div>
@@ -1997,7 +1998,11 @@
</el-input>
</el-form-item>
<el-form-item label="其他费用合计">
<el-input v-model="dispatchItemForm.otherFeeTotal" placeholder="请输入">
<el-input
v-model="dispatchItemForm.otherFeeTotal"
placeholder="请输入"
@input="value => handleDispatchNumberInput('otherFeeTotal', value)"
>
<template #suffix>{{ dispatchItemFreightCurrencyLabel }}</template>
</el-input>
</el-form-item>
@@ -2547,8 +2552,8 @@
</el-button>
</div>
<div class="map-picker-content">
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
<div ref="transportAmap" class="transport-plan-page__map"></div>
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
</div>
<div class="transport-plan-page__map-info">
<span>{{ transportMapStatus }}</span>
@@ -2729,13 +2734,14 @@ import {
getList as getProjectList,
} from '@/api/business/project-apply';
import { getList as getCommonRouteList } from '@/api/business/common-route';
import { getList as getDriverList } from '@/api/transportCapacity/driver';
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
import { getDictionary } from '@/api/system/dictbiz';
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
import { addressTypeOptions } from '@/option/base/common-address';
import { packageOptions } from '@/option/business/common';
import { formatUpdateUserName } from '@/utils/audit';
import { getToken } from '@/utils/auth';
import { normalizeMapSearchResults } from '@/utils/map-search';
import { applyTableMenuWidth } from '@/utils/table-menu';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { isMobile } from '@/utils/validate';
@@ -3284,6 +3290,23 @@ export default {
isTransportPlanDetailLayout() {
return this.detailBox;
},
detailStatusTextClass() {
const status = this.detailRow?.businessStatus;
if (status === 2 || status === '2' || status === 'completed') {
return 'status-text-success';
}
if (status === 1 || status === '1' || status === 'dispatching') {
return 'is-dispatching';
}
if (
status === 3 ||
status === '3' ||
['rejected', 'change_rejected', 'cancelled', 'withdrawn'].includes(status)
) {
return 'is-danger';
}
return 'is-info';
},
transportPlanGoodsText() {
const text = this.formatTransportPlanGoodsInfo(this.detailRow);
return text && text !== '-' ? text : '暂无货物信息';
@@ -3526,17 +3549,19 @@ export default {
},
dispatchItemFreightTotal() {
if (this.dispatchItemForm.taskEntryMode !== 'full') {
const otherFeeTotal = Number(this.dispatchItemForm.otherFeeTotal || 0);
const otherFeeRaw = this.normalizeDispatchFeeValue(this.dispatchItemForm.otherFeeTotal);
const otherFeeTotal = Number(otherFeeRaw || 0);
const freight = this.dispatchItemFreightSubtotal;
const hasFreight = freight !== '';
const hasOtherFee = this.dispatchItemForm.otherFeeTotal !== '';
const hasOtherFee = otherFeeRaw !== '';
if (!hasFreight && !hasOtherFee) return '';
const total = (hasFreight ? Number(freight) : 0) + (hasOtherFee ? otherFeeTotal : 0);
return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)));
}
const otherFeeTotal = Number(this.dispatchItemForm.otherFeeTotal || 0);
const otherFeeRaw = this.normalizeDispatchFeeValue(this.dispatchItemForm.otherFeeTotal);
const otherFeeTotal = Number(otherFeeRaw || 0);
const freightSubtotal = this.dispatchItemFreightSubtotal;
const hasOtherFee = this.dispatchItemForm.otherFeeTotal !== '';
const hasOtherFee = otherFeeRaw !== '';
if (freightSubtotal === '' && !hasOtherFee) return '';
const total = Number(freightSubtotal || 0) + (hasOtherFee ? otherFeeTotal : 0);
return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)));
@@ -4841,10 +4866,17 @@ export default {
},
fetchTaskDriverSuggestions(queryString, callback) {
const keyword = String(queryString || '').trim();
const carrier = this.getDispatchCarrierFilter();
if (!carrier.carrierId && !carrier.carrierName) {
callback([]);
return;
}
this.taskDriverLoading = true;
getDriverList(1, 20, { ...(keyword ? { driverName: keyword } : {}), posts: '司机' })
.then(res => {
const records = extractRecords(res);
fetchDriversByCarrierOrganizations(
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
carrier
)
.then(records => {
this.taskDriverOptions = records;
callback(
records.map(item => ({
@@ -4863,10 +4895,17 @@ export default {
},
fetchTaskEscortSuggestions(queryString, callback) {
const keyword = String(queryString || '').trim();
const carrier = this.getDispatchCarrierFilter();
if (!carrier.carrierId && !carrier.carrierName) {
callback([]);
return;
}
this.taskEscortLoading = true;
getDriverList(1, 20, { ...(keyword ? { driverName: keyword } : {}), posts: '押运员' })
.then(res => {
const records = extractRecords(res);
fetchDriversByCarrierOrganizations(
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '押运员' },
carrier
)
.then(records => {
this.taskEscortOptions = records;
callback(records.map(item => ({ ...item, value: item.driverName || item.name || '' })));
})
@@ -5738,12 +5777,7 @@ export default {
this.ensureTransportAmapGeocoder()
.then(() => this.runAmapGeocode('location', keyword))
.then(result => {
this.transportMapSearchResults = (result.geocodes || []).map((item, index) => ({
id: item.id || index,
name: item.formattedAddress || keyword,
address: item.formattedAddress || keyword,
location: item.location,
}));
this.transportMapSearchResults = normalizeMapSearchResults(result, keyword);
const point = this.resolveMapPoint(result);
if (!point) {
this.transportMapStatus = '未找到匹配地址';
@@ -7241,11 +7275,13 @@ export default {
freightInfo.freightItems?.[0]?.unitPrice ??
'';
const otherFeeTotal =
item.otherFeeTotal ??
plan.otherFeeTotal ??
freightInfo.otherFeeTotal ??
freightInfo.otherFreightAmount ??
'';
this.normalizeDispatchFeeValue(
item.otherFeeTotal ??
plan.otherFeeTotal ??
freightInfo.otherFeeTotal ??
freightInfo.otherFreightAmount ??
''
);
const quantity = Number(
item.quantity ||
item.cargoQuantity ||
@@ -7560,6 +7596,9 @@ export default {
...baseRow,
...(index >= 0 ? row : {}),
};
this.dispatchItemForm.otherFeeTotal = this.normalizeDispatchFeeValue(
this.dispatchItemForm.otherFeeTotal
);
this.dispatchItemForm.carrierType = this.dispatchItemForm.carrierType || '承运商';
this.loadDispatchCarrierOptions(this.dispatchRow).then(() => {
this.applyDispatchSelfCarrierDefaults();
@@ -7611,6 +7650,7 @@ export default {
this.dispatchItemForm.carrierName = '';
this.dispatchItemForm.carrierId = '';
this.dispatchItemForm.carrierContractId = '';
this.clearDispatchDriverVehicleFields();
if (nextValue === '承运商') {
this.dispatchItemForm.trailerVehicleNo = '';
this.dispatchItemForm.escortName = '';
@@ -7798,12 +7838,29 @@ export default {
this.dispatchItemForm.carrierContractId = carrier?.carrierContractId || '';
this.dispatchItemForm.carrierId = carrier?.carrierId || '';
this.dispatchItemForm.carrierName = carrier?.carrierName || '';
return;
} else {
this.dispatchItemForm.carrierContractId = '';
this.dispatchItemForm.carrierId = '';
this.dispatchItemForm.carrierName = carrier?.carrierName || value || '';
}
this.dispatchItemForm.carrierContractId = '';
this.dispatchItemForm.carrierId =
this.dispatchItemForm.carrierType === '自运' ? '' : carrier?.id || '';
this.dispatchItemForm.carrierName = carrier?.carrierName || value || '';
this.clearDispatchDriverVehicleFields();
},
clearDispatchDriverVehicleFields() {
this.dispatchItemForm.driverId = '';
this.dispatchItemForm.driverName = '';
this.dispatchItemForm.driverPhone = '';
this.dispatchItemForm.vehicleNo = '';
this.dispatchItemForm.trailerVehicleNo = '';
this.dispatchItemForm.escortName = '';
this.dispatchItemForm.escortPhone = '';
this.taskDriverOptions = [];
this.taskEscortOptions = [];
},
getDispatchCarrierFilter() {
return {
carrierId: this.dispatchItemForm.carrierId || '',
carrierName: this.dispatchItemForm.carrierName || '',
};
},
handleDispatchCargoQuantityInput(row, value) {
const text = String(value || '').replace(/[^\d.]/g, '');
@@ -7820,6 +7877,11 @@ export default {
this.dispatchItemForm[prop] =
parts.length > 1 ? `${parts[0]}.${parts.slice(1).join('').slice(0, 2)}` : parts[0];
},
normalizeDispatchFeeValue(value) {
if (value === undefined || value === null || value === '') return '';
if (Number(value) === -1) return '';
return value;
},
addDispatchCargoRow(index = -1) {
const nextRow = this.normalizeTransportCargoRow({ priceUnit: '元/吨' });
if (index > -1) this.dispatchItemCargoRows.splice(index + 1, 0, nextRow);
@@ -7972,6 +8034,12 @@ export default {
}, 0)
);
},
/** 精简录入提示:可调度余量扣减当前本次数量后的剩余 */
dispatchItemHintRemainingQuantity(row = {}) {
const available = this.dispatchItemRemainingQuantity(row);
const current = this.parseDispatchQuantity(row.quantity);
return Math.max(available - current, 0);
},
pruneDispatchPendingRows(rows = []) {
const planQuantities = new Map();
this.dispatchPlanGoodsRows.forEach(goods => {
@@ -8220,14 +8288,29 @@ export default {
}),
];
const firstGoods = goodsRows[0] || {};
const quantitySum = goodsRows.reduce(
(sum, goods) => sum + this.parseDispatchQuantity(goods.quantity),
0
);
const quantityText = quantitySum
? this.formatDispatchQuantity(quantitySum)
: firstGoods.quantity || this.dispatchItemForm.quantity || '';
const otherFeeTotal = this.normalizeDispatchFeeValue(this.dispatchItemForm.otherFeeTotal);
const nextRow = {
...this.dispatchItemForm,
goodsJson: JSON.stringify(goodsRows),
cargoType: firstGoods.cargoType || this.dispatchItemForm.cargoType || '',
cargoTypeCode: firstGoods.cargoTypeCode || this.dispatchItemForm.cargoTypeCode || '',
cargoTypePath: firstGoods.cargoTypePath || this.dispatchItemForm.cargoTypePath || [],
cargoName: firstGoods.cargoName || this.dispatchItemForm.cargoName || '',
quantity: firstGoods.quantity || this.dispatchItemForm.quantity || '',
cargoName:
goodsRows
.map(goods => goods.cargoName || goods.goodsName || '')
.filter(Boolean)
.join('.') ||
firstGoods.cargoName ||
this.dispatchItemForm.cargoName ||
'',
quantity: quantityText,
quantityUnit: firstGoods.quantityUnit || this.dispatchItemForm.quantityUnit || '',
unitPrice: firstGoods.unitPrice || '',
priceUnit: firstGoods.priceUnit || '',
@@ -8235,7 +8318,7 @@ export default {
freightJson: JSON.stringify({
currency: this.dispatchContractCurrency || this.dispatchItemForm.freightCurrency || 'RMB',
totalFreightAmount: this.dispatchItemFreightSubtotal,
otherFreightAmount: this.dispatchItemForm.otherFeeTotal || '',
otherFreightAmount: otherFeeTotal,
freightItems: goodsRows.map((cargo, index) => ({
cargoIndex: index,
cargoName: cargo.cargoName || '',
@@ -8251,7 +8334,7 @@ export default {
};
nextRow.cargoInfo = this.formatDispatchCargoInfo({}, nextRow);
nextRow.freight = this.dispatchItemFreightSubtotal;
nextRow.otherFeeTotal = this.dispatchItemForm.otherFeeTotal || '';
nextRow.otherFeeTotal = otherFeeTotal;
const quantityUnit = this.getDispatchQuantityUnit(nextRow);
const otherDispatchedQuantity = this.dispatchRows.reduce((total, row, index) => {
if (
@@ -9348,19 +9431,36 @@ export default {
}
}
:deep(.el-tag) {
height: 36px;
padding: 0 14px;
border: 0;
.detail-status-text {
display: inline-flex;
align-items: center;
height: 28px;
padding: 0 12px;
border-radius: 8px;
font-size: 14px;
line-height: 36px;
font-size: 13px;
line-height: 28px;
}
:deep(.is-dispatching) {
.status-text-success,
.is-dispatching {
color: #67c23a;
background: #e1f3d8;
}
.detail-transport-type {
color: #409eff;
background: #ecf5ff;
}
.is-danger {
color: #f56c6c;
background: #fef0f0;
}
.is-info {
color: #909399;
background: #f4f4f5;
}
}
&__transport-plan-detail-overview {