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

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

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 18:35:27 +08:00
parent 694fe1a433
commit a919781d4f
19 changed files with 1701 additions and 309 deletions
@@ -4007,7 +4007,7 @@
</el-input>
<div class="business-crud-page__dispatch-quantity-hint">
剩余数量:{{
formatDispatchQuantity(dispatchItemRemainingQuantity(dispatchItemForm))
formatDispatchQuantity(dispatchItemHintRemainingQuantity(dispatchItemForm))
}}
{{ dispatchItemForm.quantityUnit || '吨' }}
</div>
@@ -4064,7 +4064,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>
@@ -4996,8 +5000,8 @@
</el-button>
</div>
<div class="map-picker-content">
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
<div ref="transportAmap" class="business-crud-page__map"></div>
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
</div>
<div class="business-crud-page__map-info">
<span>{{ transportMapStatus }}</span>
@@ -5271,7 +5275,7 @@ import {
getVoucherImages as getProcessConfigVoucherImages,
} from '@/api/business/process-config';
import { getList as getCarrierCustomerList } from '@/api/vehicle/customer-archive';
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';
@@ -5279,6 +5283,7 @@ import { packageOptions } from '@/option/business/common';
import { formatUpdateUserName } from '@/utils/audit';
import { getToken } from '@/utils/auth';
import { openImportDialog } from '@/utils/import-excel';
import { normalizeMapSearchResults } from '@/utils/map-search';
import { applyTableMenuWidth } from '@/utils/table-menu';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { isMobile } from '@/utils/validate';
@@ -6340,19 +6345,21 @@ export default {
if (this.dispatchItemForm.taskEntryMode !== 'full') {
const quantity = Number(this.dispatchItemForm.quantity || 0);
const unitPrice = Number(this.dispatchItemForm.unitPrice || 0);
const otherFeeTotal = Number(this.dispatchItemForm.otherFeeTotal || 0);
const otherFeeRaw = this.normalizeDispatchFeeValue(this.dispatchItemForm.otherFeeTotal);
const otherFeeTotal = Number(otherFeeRaw || 0);
const hasFreight = this.dispatchItemForm.unitPrice !== '' && quantity > 0;
const hasOtherFee = this.dispatchItemForm.otherFeeTotal !== '';
const hasOtherFee = otherFeeRaw !== '';
if (!hasFreight && !hasOtherFee) return '';
const total = (hasFreight ? quantity * unitPrice : 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 freightTotal = this.dispatchItemCargoRows.reduce(
(total, cargo) => total + Number(this.dispatchCargoFreightAmount(cargo) || 0),
0
);
const hasOtherFee = this.dispatchItemForm.otherFeeTotal !== '';
const hasOtherFee = otherFeeRaw !== '';
if (!freightTotal && !hasOtherFee) return '';
const total = freightTotal + (hasOtherFee ? otherFeeTotal : 0);
return Number.isInteger(total) ? String(total) : String(Number(total.toFixed(2)));
@@ -8919,6 +8926,7 @@ export default {
this.form.carrierContractId = '';
}
this.form.carrierType = nextValue;
this.clearTaskDriverVehicleFields();
if (this.isWaybillDetailLayout) {
this.taskCarrierRequestId += 1;
this.taskCarrierOptions = [];
@@ -9036,25 +9044,58 @@ export default {
},
handleTaskCarrierChange(value) {
const carrier = this.taskCarrierOptions.find(item =>
[item.value, item.customerName, item.carrierName, item.fullName, item.name].some(name =>
String(name || '') === String(value || '')
)
[
item.value,
item.customerName,
item.carrierName,
item.fullName,
item.name,
item.carrierContractId,
].some(name => String(name || '') === String(value || ''))
);
if (this.isWaybillDetailLayout && this.form.carrierType === '承运商') {
this.form.carrierContractId = carrier?.carrierContractId || '';
this.form.carrierId = carrier?.carrierId || '';
this.form.carrierName = carrier?.carrierName || value || '';
return;
} else {
this.form.carrierId = carrier?.id || carrier?.carrierId || '';
this.form.carrierName = value || '';
}
this.form.carrierId = carrier?.id || '';
this.form.carrierName = value || '';
this.clearTaskDriverVehicleFields();
},
clearTaskDriverVehicleFields() {
this.form.driverId = '';
this.form.driverName = '';
this.form.driverPhone = '';
this.form.vehicleNo = '';
this.form.trailerVehicleNo = '';
this.form.escortName = '';
this.form.escortPhone = '';
this.taskDriverOptions = [];
},
getActiveCarrierFilter() {
if (this.dispatchItemBox) {
return {
carrierId: this.dispatchItemForm.carrierId || '',
carrierName: this.dispatchItemForm.carrierName || '',
};
}
return {
carrierId: this.form.carrierId || '',
carrierName: this.form.carrierName || '',
};
},
loadTaskDriverOptions() {
if (!this.taskInfoFormEnabled || this.taskDriverLoading) return Promise.resolve([]);
const carrier = this.getActiveCarrierFilter();
if (!carrier.carrierId && !carrier.carrierName) {
this.taskDriverOptions = [];
return Promise.resolve([]);
}
this.taskDriverLoading = true;
return getDriverList(1, 9999, {})
.then(res => {
this.taskDriverOptions = extractRecords(res);
return fetchDriversByCarrierOrganizations({ size: 9999, posts: '司机' }, carrier)
.then(records => {
this.taskDriverOptions = records;
return this.taskDriverOptions;
})
.finally(() => {
@@ -9063,10 +9104,17 @@ export default {
},
fetchTaskDriverSuggestions(queryString, callback) {
const keyword = String(queryString || '').trim();
const carrier = this.getActiveCarrierFilter();
if (!carrier.carrierId && !carrier.carrierName) {
callback([]);
return;
}
this.taskDriverLoading = true;
getDriverList(1, 20, keyword ? { driverName: keyword } : {})
.then(res => {
const records = extractRecords(res);
fetchDriversByCarrierOrganizations(
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
carrier
)
.then(records => {
this.taskDriverOptions = records;
callback(
records.map(item => ({
@@ -10239,12 +10287,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 = '未找到匹配地址';
@@ -12760,6 +12803,9 @@ export default {
...baseRow,
...(index >= 0 ? row : {}),
};
this.dispatchItemForm.otherFeeTotal = this.normalizeDispatchFeeValue(
this.dispatchItemForm.otherFeeTotal
);
this.dispatchItemForm.carrierType = this.dispatchItemForm.carrierType || '承运商';
const goodsRows = this.parseJsonArray(this.dispatchItemForm.goodsJson);
this.dispatchItemCargoRows = (goodsRows.length ? goodsRows : [this.dispatchItemForm]).map(
@@ -12816,6 +12862,7 @@ export default {
}
}
this.dispatchItemForm.carrierType = nextValue;
this.clearDispatchDriverVehicleFields();
this.loadDispatchCarrierOptions(this.dispatchRow);
},
isDispatchCarrierRequired(carrierType) {
@@ -12945,20 +12992,39 @@ export default {
});
if (this.dispatchIsCarrierMode) {
this.dispatchItemForm.carrierContractId = carrier?.carrierContractId || '';
this.dispatchItemForm.carrierId = '';
this.dispatchItemForm.carrierId = carrier?.carrierId || '';
this.dispatchItemForm.carrierName = carrier?.carrierName || '';
return;
} else {
this.dispatchItemForm.carrierContractId = '';
this.dispatchItemForm.carrierId = carrier?.id || carrier?.carrierId || '';
this.dispatchItemForm.carrierName = carrier?.carrierName || value || '';
}
this.dispatchItemForm.carrierContractId = '';
this.dispatchItemForm.carrierId = 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 = [];
},
loadDispatchDriverOptions() {
if (this.taskDriverLoading) return Promise.resolve([]);
const carrier = {
carrierId: this.dispatchItemForm.carrierId || '',
carrierName: this.dispatchItemForm.carrierName || '',
};
if (!carrier.carrierId && !carrier.carrierName) {
this.taskDriverOptions = [];
return Promise.resolve([]);
}
this.taskDriverLoading = true;
return getDriverList(1, 9999, {})
.then(res => {
this.taskDriverOptions = extractRecords(res);
return fetchDriversByCarrierOrganizations({ size: 9999, posts: '司机' }, carrier)
.then(records => {
this.taskDriverOptions = records;
return this.taskDriverOptions;
})
.finally(() => {
@@ -13035,6 +13101,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);
@@ -13118,6 +13189,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 => {
@@ -13364,14 +13441,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 || '',
@@ -13379,7 +13471,7 @@ export default {
freightJson: JSON.stringify({
currency: this.dispatchItemForm.freightCurrency || 'CNY',
totalFreightAmount: this.dispatchItemFreightSubtotal,
otherFreightAmount: this.dispatchItemForm.otherFeeTotal || '',
otherFreightAmount: otherFeeTotal,
freightItems: goodsRows.map((cargo, index) => ({
cargoIndex: index,
cargoName: cargo.cargoName || '',
@@ -13395,7 +13487,7 @@ export default {
};
nextRow.cargoInfo = this.formatDispatchCargoInfo({}, nextRow);
nextRow.freight = this.getDispatchFeeFields({}, nextRow).freight;
nextRow.otherFeeTotal = this.dispatchItemForm.otherFeeTotal || '';
nextRow.otherFeeTotal = otherFeeTotal;
const quantityUnit = this.getDispatchQuantityUnit(nextRow);
const otherDispatchedQuantity = this.dispatchRows.reduce((total, row, index) => {
if (
@@ -156,11 +156,11 @@
<el-row :gutter="16">
<template v-if="isRoad(route)">
<el-col :span="6"><el-form-item label="承运商" :required="route.carrierType !== '自运'"><el-select :model-value="route.carrierType !== '自运' ? route.carrierContractId : route.carrierName" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="carrierOptionValue(item)" :label="carrierOptionLabel(item)" :value="route.carrierType !== '自运' ? carrierOptionValue(item) : item.carrierName" /></el-select></el-form-item></el-col>
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="fetchDriverSuggestions" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="(query, cb) => fetchDriverSuggestions(route, query, cb)" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col>
<el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="挂车车牌号"><el-input v-model="route.trailerVehicleNo" placeholder="请输入" /></el-form-item></el-col>
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人"><el-autocomplete v-model="route.escortName" :debounce="300" :fetch-suggestions="fetchEscortSuggestions" clearable placeholder="请输入" :loading="escortLoading" @select="item => handleEscortSuggestionSelect(route, item)" /></el-form-item></el-col>
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人"><el-autocomplete v-model="route.escortName" :debounce="300" :fetch-suggestions="(query, cb) => fetchEscortSuggestions(route, query, cb)" clearable placeholder="请输入" :loading="escortLoading" @select="item => handleEscortSuggestionSelect(route, item)" /></el-form-item></el-col>
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人手机号"><el-input v-model="route.escortPhone" placeholder="请输入" /></el-form-item></el-col>
</template>
<template v-else>
@@ -233,7 +233,7 @@
<script>
import * as api from '@/api/business/master-order';
import { getList as getDriverList } from '@/api/transportCapacity/driver';
import { fetchDriversByCarrierOrganizations } from '@/utils/carrier-org-resource';
import { isMobile } from '@/utils/validate';
const unwrapRecords = res => {
@@ -290,7 +290,7 @@ export default {
},
},
async mounted() {
await Promise.all([this.load(), this.loadCarrierOptions(), this.loadDriverOptions()]);
await Promise.all([this.load(), this.loadCarrierOptions()]);
this.applySelfOperatedCarrierDefaults();
},
methods: {
@@ -727,6 +727,7 @@ export default {
route.carrierName = '';
route.carrierId = '';
route.carrierContractId = '';
this.clearRouteDriverVehicleFields(route);
if (value === '承运商') {
route.trailerVehicleNo = '';
route.escortName = '';
@@ -746,6 +747,24 @@ export default {
route.carrierContractId = route.carrierType !== '自运' ? contract?.contractId || '' : '';
route.carrierId = route.carrierType !== '自运' ? contract?.carrierId || '' : '';
route.carrierName = contract?.carrierName || '';
this.clearRouteDriverVehicleFields(route);
},
clearRouteDriverVehicleFields(route) {
if (!route) return;
route.driverId = '';
route.driverName = '';
route.driverPhone = '';
route.vehicleNo = '';
route.trailerVehicleNo = '';
route.escortName = '';
route.escortPhone = '';
this.driverOptions = [];
},
getRouteCarrierFilter(route = {}) {
return {
carrierId: route.carrierId || '',
carrierName: route.carrierName || '',
};
},
carrierOptionValue(item = {}) {
return item.contractId || item.id || item.value || item.carrierName || '';
@@ -817,18 +836,30 @@ export default {
route.carrierContractId = '';
});
},
async loadDriverOptions(keyword = '') {
async loadDriverOptions(route = {}, keyword = '') {
const carrier = this.getRouteCarrierFilter(route);
if (!carrier.carrierId && !carrier.carrierName) {
this.driverOptions = [];
return [];
}
this.driverLoading = true;
try {
this.driverOptions = unwrapRecords(
await getDriverList(1, 20, { driverName: keyword, posts: '司机' })
this.driverOptions = await fetchDriversByCarrierOrganizations(
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '司机' },
carrier
);
return this.driverOptions;
} finally {
this.driverLoading = false;
}
},
fetchDriverSuggestions(queryString, callback) {
this.loadDriverOptions(String(queryString || '').trim())
fetchDriverSuggestions(route, queryString, callback) {
const carrier = this.getRouteCarrierFilter(route);
if (!carrier.carrierId && !carrier.carrierName) {
callback([]);
return;
}
this.loadDriverOptions(route, String(queryString || '').trim())
.then(() =>
callback(
this.driverOptions.map(item => ({
@@ -846,13 +877,21 @@ export default {
const drivingVehicle = String(item.drivingVehicle || '').trim();
if (drivingVehicle) route.vehicleNo = drivingVehicle;
},
fetchEscortSuggestions(queryString, callback) {
fetchEscortSuggestions(route, queryString, callback) {
const keyword = String(queryString || '').trim();
const carrier = this.getRouteCarrierFilter(route);
if (!carrier.carrierId && !carrier.carrierName) {
callback([]);
return;
}
this.escortLoading = true;
getDriverList(1, 20, { ...(keyword ? { driverName: keyword } : {}), posts: '押运员' })
.then(res => {
fetchDriversByCarrierOrganizations(
{ size: 20, ...(keyword ? { driverName: keyword } : {}), posts: '押运员' },
carrier
)
.then(records => {
callback(
unwrapRecords(res).map(item => ({
records.map(item => ({
...item,
value: this.driverOptionLabel(item),
}))
@@ -402,8 +402,8 @@
>
</div>
<div class="map-picker-content">
<map-search-results :results="mapSearchResults" @select="selectAddressMapSearchResult" />
<div ref="addressMap" class="address-map" />
<map-search-results :results="mapSearchResults" @select="selectAddressMapSearchResult" />
</div>
<div class="address-map-status">
{{ mapStatus
@@ -601,6 +601,7 @@ let amapLoader;
import { getList as getCommonRouteList } from '@/api/business/common-route';
import * as api from '@/api/business/master-order';
import { formatUpdateUserName } from '@/utils/audit';
import { normalizeMapSearchResults } from '@/utils/map-search';
import { isMobile } from '@/utils/validate';
export default {
components: {
@@ -1285,14 +1286,8 @@ export default {
)
)
.then(result => {
const geocodes = result?.geocodes || [];
this.mapSearchResults = geocodes.map((item, index) => ({
id: item.id || index,
name: item.formattedAddress || this.mapKeyword,
address: item.formattedAddress || this.mapKeyword,
location: item.location,
}));
const point = geocodes[0]?.location;
this.mapSearchResults = normalizeMapSearchResults(result, this.mapKeyword);
const point = this.mapSearchResults[0]?.location;
if (!point) return this.$message.warning('地图搜索无匹配地址');
this.pickAddressMap(point, this.mapKeyword);
})
@@ -2199,8 +2194,15 @@ export default {
display: flex;
gap: 8px;
margin-bottom: 12px;
.el-input {
flex: 1;
min-width: 0;
}
}
.address-map {
width: 100%;
min-width: 0;
height: 440px;
}
.address-map-status {
@@ -931,11 +931,11 @@
/><el-button type="primary" @click="searchTransportMapKeyword">搜索</el-button>
</div>
<div class="map-picker-content">
<div ref="transportMap" class="shipping-template-page__map" />
<map-search-results
:results="transportMapSearchResults"
@select="selectTransportMapSearchResult"
/>
<div ref="transportMap" class="shipping-template-page__map" />
</div>
<div class="shipping-template-page__map-info">{{ transportMapStatus }}</div>
<template #footer
@@ -1098,6 +1098,7 @@ import {
import { getDictionary as getSystemDictionary } from '@/api/system/dict';
import { getDictionary } from '@/api/system/dictbiz';
import { packageOptions } from '@/option/business/common';
import { normalizeMapSearchResults } from '@/utils/map-search';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { isMobile } from '@/utils/validate';
import { Location } from '@element-plus/icons-vue';
@@ -2615,12 +2616,7 @@ export default {
.then(() => this.ensureTransportMapGeocoder())
.then(() => this.runTransportMapGeocode('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.resolveTransportMapPoint(result);
if (!point) throw new Error('未找到匹配地址');
return this.$nextTick().then(() => {
@@ -3754,6 +3750,12 @@ export default {
.shipping-template-page__map-toolbar {
display: flex;
gap: 8px;
margin-bottom: 12px;
.el-input {
flex: 1;
min-width: 0;
}
}
.shipping-template-page__dialog-search {
margin-bottom: 8px;
@@ -3766,11 +3768,9 @@ export default {
justify-content: flex-end;
}
.shipping-template-page__map {
flex: 1 1 auto;
width: 100%;
min-width: 0;
height: 480px;
margin-top: 12px;
}
:deep(.shipping-template-page__map > .amap-container) {
width: 100% !important;
@@ -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 {
@@ -1939,12 +1939,17 @@
}}</strong
>
</div>
<div
v-if="!waybillIsCarrier(detailRow)"
class="waybill-manage-page__waybill-detail-field"
>
<div class="waybill-manage-page__waybill-detail-field">
<span>里程(km)</span
><strong>{{ normalizeMileageValue(detailRow.mileage) || '-' }}</strong>
><strong>{{ waybillDetailMileage(detailRow) || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>计划发货日期</span
><strong>{{ waybillDetailEstimatedStartTime(detailRow) || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>计划完成日期</span
><strong>{{ waybillDetailEstimatedEndTime(detailRow) || '-' }}</strong>
</div>
</template>
<template v-else-if="waybillIsNonRoadTransport(detailRow)">
@@ -1966,12 +1971,32 @@
<div class="waybill-manage-page__waybill-detail-field">
<span>仓位</span><strong>{{ detailRow.cabinNo || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>预计发货日期</span
><strong>{{ waybillDetailEstimatedStartTime(detailRow) || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>预计完成日期</span
><strong>{{ waybillDetailEstimatedEndTime(detailRow) || '-' }}</strong>
</div>
</template>
<template v-else>
<div class="waybill-manage-page__waybill-detail-field">
<span>{{ waybillVehicleNoLabel(detailRow) }}</span>
<strong>{{ detailRow.vehicleNo || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>里程(km)</span
><strong>{{ waybillDetailMileage(detailRow) || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>计划发货日期</span
><strong>{{ waybillDetailEstimatedStartTime(detailRow) || '-' }}</strong>
</div>
<div class="waybill-manage-page__waybill-detail-field">
<span>计划完成日期</span
><strong>{{ waybillDetailEstimatedEndTime(detailRow) || '-' }}</strong>
</div>
</template>
<div
class="waybill-manage-page__waybill-detail-field"
@@ -2851,8 +2876,8 @@
</el-button>
</div>
<div class="map-picker-content">
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
<div ref="transportAmap" class="waybill-manage-page__map"></div>
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
</div>
<div class="waybill-manage-page__map-info">
<span>{{ transportMapStatus }}</span>
@@ -3112,6 +3137,9 @@ import {
import { getPunchRecords as getWaybillPunchRecords, locateVehicle, trackVehicle } from '@/api/business/waybill-manage';
import { getMkPublicDetail } from '@/api/mk-process';
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';
@@ -3120,11 +3148,12 @@ import { formatUpdateUserName } from '@/utils/audit';
import { submitMkApprovalFlow } from '@/utils/mk-approval';
import { getToken } from '@/utils/auth';
import { openImportDialog } from '@/utils/import-excel';
import { normalizeMapSearchResults } from '@/utils/map-search';
import { applyTableMenuWidth } from '@/utils/table-menu';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { isMobile } from '@/utils/validate';
import { InfoFilled, Location, OfficeBuilding, Search, WarnTriangleFilled } from '@element-plus/icons-vue';
import { ElImageViewer } from 'element-plus';
import { ElAutocomplete, ElImageViewer } from 'element-plus';
import { OpenFileViewer } from '@open-file-viewer/vue';
import {
fallbackPlugin,
@@ -3634,6 +3663,7 @@ export default {
this.detailPageLocked = this.isStandaloneWaybillDetailPage;
this.formModeLocked = this.$route.query.mode || '';
if (this.isPublicWaybillView) return;
this.setupSearchAutocompletes();
if (this.config.enableAllDept && this.isAdmin) {
this.allDept = 1;
}
@@ -4292,8 +4322,12 @@ export default {
) {
return false;
}
// 进行中的运单隐藏取消(与 config.canCancel / isInProgressStatus 对齐)
if (typeof this.config.canCancel === 'function' && !this.config.canCancel(row)) {
return false;
}
const status = this.statusValue(row);
return ['pending', 'processing', 'running'].includes(status);
return status === 'pending';
},
canComplete(row) {
if (
@@ -4524,6 +4558,42 @@ export default {
waybillIsCarrier(row = {}) {
return String(row.carrierTypeName || row.carrierType || '').trim() === '承运商';
},
waybillDetailTaskInfo(row = {}) {
return this.parseJsonObject(row.taskInfoJson || row.taskInfo || {});
},
waybillDetailMileage(row = {}) {
const taskInfo = this.waybillDetailTaskInfo(row);
const goodsRows = this.parseJsonArray(row.goodsJson);
const firstGoods = goodsRows[0] || {};
return this.normalizeMileageValue(
row.mileage || taskInfo.mileage || firstGoods.mileage || ''
);
},
waybillDetailEstimatedStartTime(row = {}) {
const taskInfo = this.waybillDetailTaskInfo(row);
const value =
row.estimatedStartTime ||
row.planStartDate ||
taskInfo.estimatedStartTime ||
taskInfo.planStartDate ||
'';
return this.formatWaybillDetailDate(value);
},
waybillDetailEstimatedEndTime(row = {}) {
const taskInfo = this.waybillDetailTaskInfo(row);
const value =
row.estimatedEndTime ||
row.planEndDate ||
taskInfo.estimatedEndTime ||
taskInfo.planEndDate ||
'';
return this.formatWaybillDetailDate(value);
},
formatWaybillDetailDate(value) {
const text = String(value || '').trim();
if (!text) return '';
return text.slice(0, 10);
},
waybillVehicleNoLabel(row = {}) {
return this.waybillIsRoadTransport(row) ? '车牌号' : '船/航/班列号';
},
@@ -6398,6 +6468,15 @@ export default {
this.form.carrierName = '';
this.form.carrierId = '';
this.form.carrierContractId = '';
this.form.driverId = '';
this.form.driverName = '';
this.form.driverPhone = '';
this.form.vehicleNo = '';
this.form.trailerVehicleNo = '';
this.form.escortName = '';
this.form.escortPhone = '';
this.taskDriverOptions = [];
this.taskEscortOptions = [];
if (nextValue === '承运商') {
this.form.trailerVehicleNo = '';
this.form.escortName = '';
@@ -6651,17 +6730,36 @@ export default {
this.form.carrierContractId = carrier?.carrierContractId || '';
this.form.carrierId = carrier?.carrierId || '';
this.form.carrierName = carrier?.carrierName || '';
return;
} else {
this.form.carrierId = carrier?.carrierContractId ? '' : carrier?.id || '';
this.form.carrierName = value || '';
}
this.form.carrierId = carrier?.carrierContractId ? '' : carrier?.id || '';
this.form.carrierName = value || '';
// 承运商变更后清空司机/车辆,避免跨组织脏数据
this.form.driverId = '';
this.form.driverName = '';
this.form.driverPhone = '';
this.form.vehicleNo = '';
this.form.trailerVehicleNo = '';
this.form.escortName = '';
this.form.escortPhone = '';
this.taskDriverOptions = [];
this.taskEscortOptions = [];
},
getTaskCarrierFilter() {
return {
carrierId: this.form.carrierId || '',
carrierName: this.form.carrierName || '',
};
},
loadTaskDriverOptions() {
if (!this.taskInfoFormEnabled || this.taskDriverLoading) return Promise.resolve([]);
this.taskDriverLoading = true;
return getDriverList(1, 9999, { posts: '司机' })
.then(res => {
this.taskDriverOptions = extractRecords(res);
return fetchDriversByCarrierOrganizations(
{ size: 9999, posts: '司机' },
this.getTaskCarrierFilter()
)
.then(records => {
this.taskDriverOptions = records;
return this.taskDriverOptions;
})
.finally(() => {
@@ -6670,10 +6768,17 @@ export default {
},
fetchTaskDriverSuggestions(queryString, callback) {
const keyword = String(queryString || '').trim();
const carrier = this.getTaskCarrierFilter();
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 => ({
@@ -6692,10 +6797,17 @@ export default {
},
fetchTaskEscortSuggestions(queryString, callback) {
const keyword = String(queryString || '').trim();
const carrier = this.getTaskCarrierFilter();
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 || '' })));
})
@@ -7835,12 +7947,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 = '未找到匹配地址';
@@ -8463,6 +8570,123 @@ export default {
this.page.currentPage = 1;
this.onLoad(this.page);
},
setupSearchAutocompletes() {
const fields = [
{
prop: 'projectName',
placeholder: '请选择或输入项目名称',
fetch: (queryString, callback) =>
this.fetchSearchNameSuggestions(queryString, callback, {
request: keyword =>
getProjectList(1, 20, keyword ? { projectName: keyword } : {}),
getValue: item => item.projectName || item.name || '',
}),
},
{
prop: 'customerName',
placeholder: '请选择或输入客户名称',
fetch: (queryString, callback) =>
this.fetchSearchNameSuggestions(queryString, callback, {
request: keyword =>
getCustomerArchiveList(1, 20, {
...(keyword ? { fullName: keyword } : {}),
customerType: '客户',
}),
getValue: item => item.fullName || item.customerName || item.name || '',
}),
},
{
prop: 'cargoType',
placeholder: '请选择或输入货物类型',
fetch: (queryString, callback) =>
this.fetchSearchNameSuggestions(queryString, callback, {
request: keyword =>
getCargoTypeList(1, 20, keyword ? { cargoName: keyword } : {}),
getValue: item => item.cargoName || item.typeName || item.name || item.label || '',
}),
},
{
prop: 'carrierName',
placeholder: '请选择或输入承运商名称',
fetch: (queryString, callback) =>
this.fetchSearchNameSuggestions(queryString, callback, {
request: keyword =>
getCustomerArchiveList(1, 20, {
...(keyword ? { fullName: keyword } : {}),
customerType: '承运商',
}),
getValue: item =>
item.fullName || item.carrierName || item.customerName || item.name || '',
}),
},
{
prop: 'driverName',
placeholder: '请选择或输入司机名称',
fetch: (queryString, callback) =>
this.fetchSearchNameSuggestions(queryString, callback, {
request: keyword =>
getDriverList(1, 20, {
...(keyword ? { driverName: keyword } : {}),
posts: '司机',
}),
getValue: item => item.driverName || item.name || '',
}),
},
{
prop: 'planName',
placeholder: '请选择或输入计划名称',
fetch: (queryString, callback) =>
this.fetchSearchNameSuggestions(queryString, callback, {
request: keyword =>
getShippingPlanList(1, 20, keyword ? { planName: keyword } : {}),
getValue: item => item.planName || item.name || '',
}),
},
];
fields.forEach(field => {
const column = this.findColumn?.(this.option.column, field.prop);
if (!column) return;
column.searchPlaceholder = field.placeholder;
column.renderSearch = scope =>
this.renderSearchAutocomplete(scope, field.prop, field.placeholder, field.fetch);
});
},
renderSearchAutocomplete(scope, prop, placeholder, fetchSuggestions) {
return h(ElAutocomplete, {
modelValue: scope.row?.[prop] ?? '',
'onUpdate:modelValue': value => {
if (scope.row) scope.row[prop] = value ?? '';
},
fetchSuggestions,
debounce: 300,
clearable: true,
triggerOnFocus: true,
placeholder,
style: 'width: 100%',
valueKey: 'value',
fitInputWidth: true,
});
},
fetchSearchNameSuggestions(queryString, callback, { request, getValue }) {
const keyword = String(queryString || '').trim();
Promise.resolve(request(keyword))
.then(res => {
const records = extractRecords(res);
const seen = new Set();
const options = [];
records.forEach(item => {
const value = String(getValue(item) || '').trim();
if (!value || seen.has(value)) return;
seen.add(value);
options.push({ ...item, value });
});
callback(options);
})
.catch(error => {
window.console.log(error);
callback([]);
});
},
searchChange(params, done) {
this.query = {
...params,
@@ -8728,10 +8952,21 @@ export default {
},
fetchReassignDriverSuggestions(queryString, callback) {
const keyword = String(queryString || '').trim();
const row = this.reassignDrawer.row || {};
const carrier = {
carrierId: row.carrierId || '',
carrierName: row.carrierName || '',
};
if (!carrier.carrierId && !carrier.carrierName) {
callback([]);
return;
}
this.reassignDrawer.driverLoading = 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.reassignDriverOptions = records;
callback(
records.map(item => ({