✨ 计费/调度/结算支持多方客商与承运商区分

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-02 21:12:56 +08:00
parent 8388a55481
commit c0ba9ba39b
6 changed files with 337 additions and 90 deletions
@@ -12,6 +12,9 @@
<el-descriptions-item label="方案名称">{{ <el-descriptions-item label="方案名称">{{
displayValue(draft.planName) displayValue(draft.planName)
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item label="适用客商">{{
displayValue(draft.customerName || partyOptionLabel(draft.partyRowId))
}}</el-descriptions-item>
<el-descriptions-item label="运输方式">{{ <el-descriptions-item label="运输方式">{{
transportModeLabel(draft.transportMode) transportModeLabel(draft.transportMode)
}}</el-descriptions-item> }}</el-descriptions-item>
@@ -37,6 +40,20 @@
:disabled="readonly" /></el-form-item :disabled="readonly" /></el-form-item
></el-col> ></el-col>
<el-col :span="8" <el-col :span="8"
><el-form-item label="适用客商" prop="partyRowId"
><el-select
v-model="draft.partyRowId"
clearable
filterable
placeholder="请选择适用客商"
@change="handlePartyChange"
><el-option
v-for="item in partyOptions"
:key="item.value"
:label="item.label"
:value="item.value" /></el-select></el-form-item
></el-col>
<el-col :span="8"
><el-form-item label="运输方式" prop="transportMode" ><el-form-item label="运输方式" prop="transportMode"
><el-select ><el-select
v-model="draft.transportMode" v-model="draft.transportMode"
@@ -433,6 +450,7 @@ export default {
index: { type: Number, default: -1 }, index: { type: Number, default: -1 },
readonly: Boolean, readonly: Boolean,
settlementCurrency: { type: String, default: 'RMB' }, settlementCurrency: { type: String, default: 'RMB' },
partyOptions: { type: Array, default: () => [] },
}, },
emits: ['update:modelValue', 'save'], emits: ['update:modelValue', 'save'],
data() { data() {
@@ -476,6 +494,7 @@ export default {
cargoRequest: null, cargoRequest: null,
formRules: { formRules: {
planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }], planName: [{ required: true, message: '请输入方案名称', trigger: 'blur' }],
partyRowId: [{ required: true, message: '请选择适用客商', trigger: 'change' }],
transportMode: [{ required: true, message: '请选择运输方式', trigger: 'change' }], transportMode: [{ required: true, message: '请选择运输方式', trigger: 'change' }],
}, },
}; };
@@ -549,6 +568,10 @@ export default {
normalizePlan(value = {}) { normalizePlan(value = {}) {
const plan = { const plan = {
planName: '', planName: '',
partyRowId: '',
partyRole: '',
customerId: '',
customerName: '',
transportMode: '', transportMode: '',
transportModeLabel: '', transportModeLabel: '',
defaultPlan: false, defaultPlan: false,
@@ -560,6 +583,21 @@ export default {
); );
return plan; return plan;
}, },
partyOptionLabel(partyRowId) {
const matched = (this.partyOptions || []).find(
item => String(item.value) === String(partyRowId)
);
return matched?.label || '';
},
handlePartyChange(partyRowId) {
const matched = (this.partyOptions || []).find(
item => String(item.value) === String(partyRowId)
);
this.draft.partyRowId = matched?.partyRowId || matched?.value || '';
this.draft.partyRole = matched?.partyRole || '';
this.draft.customerId = matched?.customerId || '';
this.draft.customerName = matched?.customerName || '';
},
normalizeRule(rule = {}) { normalizeRule(rule = {}) {
const next = { ...defaultRule(), ...clone(rule) }; const next = { ...defaultRule(), ...clone(rule) };
if (next.billingType === '阶梯单价' && next.minimumBillingWeight === '') { if (next.billingType === '阶梯单价' && next.minimumBillingWeight === '') {
@@ -1032,6 +1070,11 @@ export default {
this.$refs.formRef.validate(valid => { this.$refs.formRef.validate(valid => {
if (!valid || !this.validateRules()) return; if (!valid || !this.validateRules()) return;
const plan = clone(this.draft); const plan = clone(this.draft);
this.handlePartyChange(plan.partyRowId);
plan.partyRowId = this.draft.partyRowId;
plan.partyRole = this.draft.partyRole;
plan.customerId = this.draft.customerId;
plan.customerName = this.draft.customerName;
const transportMode = this.transportModeOptions.find( const transportMode = this.transportModeOptions.find(
item => String(item.value) === String(plan.transportMode) item => String(item.value) === String(plan.transportMode)
); );
@@ -195,7 +195,7 @@
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form"> <el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form">
<el-row :gutter="16"> <el-row :gutter="16">
<template v-if="isRoad(route)"> <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-select :model-value="route.carrierType !== '自运' ? carrierSelectedValue(route) : 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-col :span="6"
><el-form-item label="司机" :required="route.carrierType !== '承运商'" ><el-form-item label="司机" :required="route.carrierType !== '承运商'"
><el-autocomplete ><el-autocomplete
@@ -255,7 +255,7 @@
></el-col> ></el-col>
</template> </template>
<template v-else> <template v-else>
<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-select :model-value="route.carrierType !== '自运' ? carrierSelectedValue(route) : 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><el-input v-model="route.vehicleNo" 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 :span="6"><el-form-item label="船长"><el-input v-model="route.captainName" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="船长"><el-input v-model="route.captainName" placeholder="请输入" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="联系电话"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="联系电话"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
@@ -920,6 +920,8 @@ export default {
route.carrierName = ''; route.carrierName = '';
route.carrierId = ''; route.carrierId = '';
route.carrierContractId = ''; route.carrierContractId = '';
route.carrierPartyId = '';
route.carrierPartyRole = '';
this.clearRouteDriverVehicleFields(route); this.clearRouteDriverVehicleFields(route);
if (value === '承运商') { if (value === '承运商') {
route.trailerVehicleNo = ''; route.trailerVehicleNo = '';
@@ -930,15 +932,18 @@ export default {
if (value === '自运') this.applySelfOperatedCarrierDefaults(route); if (value === '自运') this.applySelfOperatedCarrierDefaults(route);
}); });
}, },
handleCarrierContractChange(route, contractId) { handleCarrierContractChange(route, selectedValue) {
const contract = this.carrierOptions.find( const contract = this.carrierOptions.find(
item => item =>
this.carrierOptionValue(item) === String(selectedValue || '') ||
[item.contractId, item.carrierName, item.id].some( [item.contractId, item.carrierName, item.id].some(
value => String(value || '') === String(contractId || '') value => String(value || '') === String(selectedValue || '')
) )
); );
route.carrierContractId = route.carrierType !== '自运' ? contract?.contractId || '' : ''; route.carrierContractId = route.carrierType !== '自运' ? contract?.contractId || '' : '';
route.carrierId = route.carrierType !== '自运' ? contract?.carrierId || '' : ''; route.carrierPartyId = route.carrierType !== '自运' ? contract?.carrierPartyId || '' : '';
route.carrierPartyRole = route.carrierType !== '自运' ? contract?.carrierPartyRole || '' : '';
route.carrierId = route.carrierType !== '自运' ? contract?.carrierId || contract?.customerId || '' : '';
route.carrierName = contract?.carrierName || ''; route.carrierName = contract?.carrierName || '';
this.clearRouteDriverVehicleFields(route); this.clearRouteDriverVehicleFields(route);
}, },
@@ -954,15 +959,23 @@ export default {
this.driverOptions = []; this.driverOptions = [];
}, },
carrierOptionValue(item = {}) { carrierOptionValue(item = {}) {
return item.contractId || item.id || item.value || item.carrierName || ''; return [item.contractId || item.id || '', item.carrierPartyId || '', item.carrierName || ''].join(
'__'
);
},
carrierSelectedValue(route = {}) {
return this.carrierOptionValue({
contractId: route.carrierContractId,
carrierPartyId: route.carrierPartyId,
carrierName: route.carrierName,
});
}, },
carrierOptionLabel(item) { carrierOptionLabel(item) {
const duplicateCount = this.carrierOptions.filter(option => option.carrierName === item.carrierName).length; const roleLabel = item.carrierPartyRole === 'C' ? '丙方' : item.carrierPartyRole === 'B' ? '乙方' : '';
if (duplicateCount <= 1) { const nameWithRole =
return item.carrierName; roleLabel && item.carrierName ? `${item.carrierName}(${roleLabel})` : item.carrierName;
}
const contractLabel = formatContractOptionLabel(item); const contractLabel = formatContractOptionLabel(item);
return contractLabel ? `${item.carrierName}(${contractLabel})` : item.carrierName; return contractLabel ? `${nameWithRole} - ${contractLabel}` : nameWithRole;
}, },
driverOptionLabel(item) { return item.driverName || item.name || ''; }, driverOptionLabel(item) { return item.driverName || item.name || ''; },
driverOptionKey(item) { return item.id || item.driverId || this.driverOptionLabel(item) || item.mobile; }, driverOptionKey(item) { return item.id || item.driverId || this.driverOptionLabel(item) || item.mobile; },
@@ -983,19 +996,22 @@ export default {
...item, ...item,
contractId: item.contractId || item.carrierContractId || item.id || '', contractId: item.contractId || item.carrierContractId || item.id || '',
carrierName: carrierName:
item.carrierName ||
item.partyB || item.partyB ||
item.partyBName || item.partyBName ||
item.contractPartyB || item.contractPartyB ||
item.customerContractPartyB || item.customerContractPartyB ||
item.carrierName ||
'', '',
carrierId: carrierId:
item.customerId ||
item.partyBId || item.partyBId ||
item.partyBUserId || item.partyBUserId ||
item.contractPartyBId || item.contractPartyBId ||
item.customerContractPartyBId || item.customerContractPartyBId ||
item.carrierId || item.carrierId ||
'', '',
carrierPartyId: item.carrierPartyId || '',
carrierPartyRole: item.carrierPartyRole || '',
})) }))
.filter(item => item.carrierName); .filter(item => item.carrierName);
} finally { } finally {
@@ -1137,7 +1153,7 @@ export default {
id: `${route.segmentNo}-${item.cargoName}-${Date.now()}-${Math.random()}`, id: `${route.segmentNo}-${item.cargoName}-${Date.now()}-${Math.random()}`,
batchNo, batchNo,
segmentNo: route.segmentNo, relationNo: route.segmentNo, documentType: route.documentType, transportType: route.transportType, carrierType: route.carrierType, segmentNo: route.segmentNo, relationNo: route.segmentNo, documentType: route.documentType, transportType: route.transportType, carrierType: route.carrierType,
carrierContractId: route.carrierContractId, carrierName: route.carrierName, driverName: route.driverName, driverPhone: route.driverPhone, vehicleNo: route.vehicleNo, captainName: route.captainName, cabinNo: route.cabinNo, containerNo: route.containerNo, trailerVehicleNo: route.trailerVehicleNo, escortName: route.escortName, escortPhone: route.escortPhone, mileage: this.normalizeMileage(route.mileage), unitPrice: this.freightItemsForGoods(route, item).unitPrice || '', priceUnit: this.freightItemsForGoods(route, item).priceUnit || '', currency: route.currency || 'RMB', freightAmount: this.freightAmount(this.freightItemsForGoods(route, item)), freightTotal: this.freightTotal(route), otherFeeTotal: route.otherFeeTotal || '', freightJson: this.buildFreightJson(route), routeRemark: route.remark, carrierContractId: route.carrierContractId, carrierPartyId: route.carrierPartyId, carrierPartyRole: route.carrierPartyRole, carrierName: route.carrierName, driverName: route.driverName, driverPhone: route.driverPhone, vehicleNo: route.vehicleNo, captainName: route.captainName, cabinNo: route.cabinNo, containerNo: route.containerNo, trailerVehicleNo: route.trailerVehicleNo, escortName: route.escortName, escortPhone: route.escortPhone, mileage: this.normalizeMileage(route.mileage), unitPrice: this.freightItemsForGoods(route, item).unitPrice || '', priceUnit: this.freightItemsForGoods(route, item).priceUnit || '', currency: route.currency || 'RMB', freightAmount: this.freightAmount(this.freightItemsForGoods(route, item)), freightTotal: this.freightTotal(route), otherFeeTotal: route.otherFeeTotal || '', freightJson: this.buildFreightJson(route), routeRemark: route.remark,
departureName: route.departureName, departureAddress: route.departureAddress, departureContact: route.departureContact, departurePhone: route.departurePhone, departureName: route.departureName, departureAddress: route.departureAddress, departureContact: route.departureContact, departurePhone: route.departurePhone,
arrivalName: route.arrivalName, arrivalAddress: route.arrivalAddress, arrivalContact: route.arrivalContact, arrivalPhone: route.arrivalPhone, arrivalName: route.arrivalName, arrivalAddress: route.arrivalAddress, arrivalContact: route.arrivalContact, arrivalPhone: route.arrivalPhone,
estimatedStartTime: route.estimatedStartTime, estimatedEndTime: route.estimatedEndTime, estimatedStartTime: route.estimatedStartTime, estimatedEndTime: route.estimatedEndTime,
@@ -4650,9 +4650,14 @@ export default {
}, },
taskCarrierValue: { taskCarrierValue: {
get() { get() {
return this.form.carrierType === '自运' if (this.form.carrierType === '自运') {
? this.form.carrierName || '' return this.form.carrierName || '';
: this.form.carrierContractId || this.form.carrierName || ''; }
return this.taskCarrierOptionValue({
carrierContractId: this.form.carrierContractId,
carrierPartyId: this.form.carrierPartyId,
carrierName: this.form.carrierName,
});
}, },
set(value) { set(value) {
this.handleTaskCarrierChange(value); this.handleTaskCarrierChange(value);
@@ -7290,6 +7295,8 @@ export default {
this.form.carrierName = ''; this.form.carrierName = '';
this.form.carrierId = ''; this.form.carrierId = '';
this.form.carrierContractId = ''; this.form.carrierContractId = '';
this.form.carrierPartyId = '';
this.form.carrierPartyRole = '';
this.hasProjectProcessConfig = false; this.hasProjectProcessConfig = false;
this.waybillProcessConfigRows = []; this.waybillProcessConfigRows = [];
return; return;
@@ -7304,6 +7311,8 @@ export default {
this.form.carrierName = ''; this.form.carrierName = '';
this.form.carrierId = ''; this.form.carrierId = '';
this.form.carrierContractId = ''; this.form.carrierContractId = '';
this.form.carrierPartyId = '';
this.form.carrierPartyRole = '';
this.loadProjectProcessConfigState(); this.loadProjectProcessConfigState();
this.loadTaskCarrierOptions(project); this.loadTaskCarrierOptions(project);
if (this.contractSelectEnabled) { if (this.contractSelectEnabled) {
@@ -7760,6 +7769,8 @@ export default {
this.form.carrierName = ''; this.form.carrierName = '';
this.form.carrierId = ''; this.form.carrierId = '';
this.form.carrierContractId = ''; this.form.carrierContractId = '';
this.form.carrierPartyId = '';
this.form.carrierPartyRole = '';
this.form.driverId = ''; this.form.driverId = '';
this.form.driverName = ''; this.form.driverName = '';
this.form.driverPhone = ''; this.form.driverPhone = '';
@@ -7814,7 +7825,9 @@ export default {
}, },
taskCarrierOptionValue(item = {}) { taskCarrierOptionValue(item = {}) {
if (this.form.carrierType !== '自运') { if (this.form.carrierType !== '自运') {
return item.carrierContractId || item.value || item.id || ''; if (item.value) return item.value;
return [item.carrierContractId || item.id || '', item.carrierPartyId || '', item.carrierName || '']
.join('__');
} }
return ( return (
item.carrierName || item.carrierName ||
@@ -7852,9 +7865,39 @@ export default {
names.forEach(append); names.forEach(append);
return options; return options;
}, },
parseContractPartyRows(partyJson, fallbackName = '', partyRole = 'B') {
const rows = this.parseJsonArray(partyJson)
.map(item => ({
id: item.id || '',
customerId: item.customerId || '',
customerName: String(item.customerName || '').trim(),
creditCode: item.creditCode || '',
partyRole,
}))
.filter(item => item.customerName);
if (rows.length) return rows;
return String(fallbackName || '')
.split(/[、,,]/)
.map(name => name.trim())
.filter(Boolean)
.map(name => ({
id: '',
customerId: '',
customerName: name,
creditCode: '',
partyRole,
}));
},
getWaybillCarrierContractOptions(contracts = []) { getWaybillCarrierContractOptions(contracts = []) {
const carrierNameCounts = contracts.reduce((counts, contract) => { const options = [];
const carrierName = String( contracts.forEach(contract => {
if (!contract?.id) return;
const parties = [
...this.parseContractPartyRows(contract.partyBJson, contract.partyB, 'B'),
...this.parseContractPartyRows(contract.partyCJson, contract.partyC, 'C'),
];
if (!parties.length) {
const fallbackName = String(
contract.partyB || contract.partyB ||
contract.partyBName || contract.partyBName ||
contract.contractPartyB || contract.contractPartyB ||
@@ -7863,44 +7906,35 @@ export default {
contract.secondPartyName || contract.secondPartyName ||
'' ''
).trim(); ).trim();
if (carrierName) counts[carrierName] = (counts[carrierName] || 0) + 1; if (fallbackName) {
return counts; parties.push({
}, {}); id: '',
return contracts customerId: '',
.map(contract => { customerName: fallbackName,
const carrierName = String( creditCode: '',
contract.partyB || partyRole: 'B',
contract.partyBName || });
contract.contractPartyB || }
contract.customerContractPartyB || }
contract.secondParty ||
contract.secondPartyName ||
''
).trim();
if (!carrierName || !contract.id) return null;
const contractIdentifier = contract.contractName || contract.contractNo || contract.id; const contractIdentifier = contract.contractName || contract.contractNo || contract.id;
return { parties.forEach(party => {
const roleLabel = party.partyRole === 'C' ? '丙方' : '乙方';
const optionValue = [contract.id, party.id || '', party.customerName].join('__');
options.push({
id: contract.id, id: contract.id,
carrierId: carrierId: party.customerId || '',
contract.partyBId || value: optionValue,
contract.partyBUserId || label: `${party.customerName}(${roleLabel}) - ${contractIdentifier}`,
contract.contractPartyBId || carrierName: party.customerName,
contract.customerContractPartyBId || fullName: party.customerName,
contract.secondPartyId ||
contract.secondPartyUserId ||
contract.carrierId ||
'',
value: contract.id,
label:
carrierNameCounts[carrierName] > 1
? `${carrierName}(${contractIdentifier})`
: carrierName,
carrierName,
fullName: carrierName,
carrierContractId: contract.id, carrierContractId: contract.id,
}; carrierPartyId: party.id || '',
}) carrierPartyRole: party.partyRole,
.filter(Boolean); creditCode: party.creditCode || '',
});
});
});
return options;
}, },
resetWaybillCarrierContractState() { resetWaybillCarrierContractState() {
this.waybillCarrierContractRequestId += 1; this.waybillCarrierContractRequestId += 1;
@@ -7930,6 +7964,8 @@ export default {
this.form.carrierName = ''; this.form.carrierName = '';
this.form.carrierId = ''; this.form.carrierId = '';
this.form.carrierContractId = ''; this.form.carrierContractId = '';
this.form.carrierPartyId = '';
this.form.carrierPartyRole = '';
} }
return options; return options;
}) })
@@ -8020,10 +8056,14 @@ export default {
); );
if (this.form.carrierType !== '自运') { if (this.form.carrierType !== '自运') {
this.form.carrierContractId = carrier?.carrierContractId || ''; this.form.carrierContractId = carrier?.carrierContractId || '';
this.form.carrierPartyId = carrier?.carrierPartyId || '';
this.form.carrierPartyRole = carrier?.carrierPartyRole || '';
this.form.carrierId = carrier?.carrierId || ''; this.form.carrierId = carrier?.carrierId || '';
this.form.carrierName = carrier?.carrierName || ''; this.form.carrierName = carrier?.carrierName || '';
} else { } else {
this.form.carrierId = carrier?.carrierContractId ? '' : carrier?.id || ''; this.form.carrierId = carrier?.carrierContractId ? '' : carrier?.id || '';
this.form.carrierPartyId = '';
this.form.carrierPartyRole = '';
this.form.carrierName = value || ''; this.form.carrierName = value || '';
} }
// 承运商变更后清空司机/车辆,避免跨组织脏数据 // 承运商变更后清空司机/车辆,避免跨组织脏数据
+61 -12
View File
@@ -1448,7 +1448,7 @@
<el-select <el-select
:model-value=" :model-value="
dialogForm.carrierType === '承运商' dialogForm.carrierType === '承运商'
? dialogForm.carrierContractId ? taskCarrierSelectedValue
: dialogForm.carrierName : dialogForm.carrierName
" "
clearable clearable
@@ -1465,7 +1465,7 @@
> >
<el-option <el-option
v-for="item in taskCarrierOptions" v-for="item in taskCarrierOptions"
:key="item.id || item.carrierName" :key="carrierOptionValue(item)"
:label=" :label="
dialogForm.carrierType === '承运商' dialogForm.carrierType === '承运商'
? formatTaskCarrierContractLabel(item) ? formatTaskCarrierContractLabel(item)
@@ -1473,7 +1473,7 @@
" "
:value=" :value="
dialogForm.carrierType === '承运商' dialogForm.carrierType === '承运商'
? item.id ? carrierOptionValue(item)
: item.carrierName || item.partyB : item.carrierName || item.partyB
" "
/> />
@@ -1953,6 +1953,8 @@ const createDialogForm = () => ({
carrierType: '承运商', carrierType: '承运商',
carrierName: '', carrierName: '',
carrierContractId: '', carrierContractId: '',
carrierPartyId: '',
carrierPartyRole: '',
escortName: '', escortName: '',
escortPhone: '', escortPhone: '',
departureAddress: '', departureAddress: '',
@@ -2256,6 +2258,13 @@ export default {
if (!this.taskCarrierLoading && !this.taskCarrierOptions.length) return '暂无可用承运商合同'; if (!this.taskCarrierLoading && !this.taskCarrierOptions.length) return '暂无可用承运商合同';
return '请选择'; return '请选择';
}, },
taskCarrierSelectedValue() {
return this.carrierOptionValue({
id: this.dialogForm.carrierContractId,
carrierPartyId: this.dialogForm.carrierPartyId,
carrierName: this.dialogForm.carrierName,
});
},
detailStatusType() { detailStatusType() {
const typeMap = { const typeMap = {
pending: 'success', pending: 'success',
@@ -2391,11 +2400,23 @@ export default {
driverOptionText, driverOptionText,
formatTaskCarrierContractLabel(item = {}) { formatTaskCarrierContractLabel(item = {}) {
const carrierName = item.carrierName || item.partyB || ''; const carrierName = item.carrierName || item.partyB || '';
const roleLabel =
item.carrierPartyRole === 'C' || item.carrierPartyRole === '丙'
? '丙方'
: item.carrierPartyRole === 'B' || item.carrierPartyRole === '乙'
? '乙方'
: '';
const contractLabel = formatContractOptionLabel(item); const contractLabel = formatContractOptionLabel(item);
if (carrierName && contractLabel) { const nameWithRole = roleLabel && carrierName ? `${carrierName}(${roleLabel})` : carrierName;
return `${carrierName}(${contractLabel})`; if (nameWithRole && contractLabel) {
return `${nameWithRole} - ${contractLabel}`;
} }
return carrierName || contractLabel; return nameWithRole || contractLabel;
},
carrierOptionValue(item = {}) {
return [item.id || '', item.carrierPartyId || '', item.carrierName || item.partyB || ''].join(
'__'
);
}, },
syncStandaloneTagTitle() { syncStandaloneTagTitle() {
if (!this.isStandalonePage) return; if (!this.isStandalonePage) return;
@@ -4555,6 +4576,8 @@ export default {
}, },
handleCarrierTypeChange() { handleCarrierTypeChange() {
this.dialogForm.carrierContractId = ''; this.dialogForm.carrierContractId = '';
this.dialogForm.carrierPartyId = '';
this.dialogForm.carrierPartyRole = '';
this.dialogForm.carrierName = ''; this.dialogForm.carrierName = '';
this.dialogForm.carrierId = ''; this.dialogForm.carrierId = '';
this.clearDialogDriverVehicleFields(); this.clearDialogDriverVehicleFields();
@@ -4565,17 +4588,25 @@ export default {
} }
if (this.dialogForm.carrierType === '自运') { if (this.dialogForm.carrierType === '自运') {
this.dialogForm.carrierContractId = ''; this.dialogForm.carrierContractId = '';
this.dialogForm.carrierPartyId = '';
this.dialogForm.carrierPartyRole = '';
} }
this.loadTaskCarrierOptions(); this.loadTaskCarrierOptions();
}, },
handleTaskCarrierChange(contractId) { handleTaskCarrierChange(selectedValue) {
const contract = this.taskCarrierOptions.find(item => const contract = this.taskCarrierOptions.find(
item =>
this.carrierOptionValue(item) === String(selectedValue || '') ||
[item.id, item.carrierName, item.partyB].some( [item.id, item.carrierName, item.partyB].some(
value => String(value || '') === String(contractId || '') value => String(value || '') === String(selectedValue || '')
) )
); );
this.dialogForm.carrierContractId = this.dialogForm.carrierContractId =
this.dialogForm.carrierType === '承运商' && contract?.id ? contract.id : ''; this.dialogForm.carrierType === '承运商' && contract?.id ? contract.id : '';
this.dialogForm.carrierPartyId =
this.dialogForm.carrierType === '承运商' ? contract?.carrierPartyId || '' : '';
this.dialogForm.carrierPartyRole =
this.dialogForm.carrierType === '承运商' ? contract?.carrierPartyRole || '' : '';
this.dialogForm.carrierName = contract?.carrierName || ''; this.dialogForm.carrierName = contract?.carrierName || '';
this.dialogForm.carrierId = contract?.carrierId || contract?.customerId || ''; this.dialogForm.carrierId = contract?.carrierId || contract?.customerId || '';
this.clearDialogDriverVehicleFields(); this.clearDialogDriverVehicleFields();
@@ -4758,30 +4789,45 @@ export default {
.map(item => ({ .map(item => ({
...item, ...item,
carrierName: carrierName:
item.carrierName ||
item.partyB || item.partyB ||
item.partyBName || item.partyBName ||
item.contractPartyB || item.contractPartyB ||
item.customerContractPartyB || item.customerContractPartyB ||
item.carrierName ||
'', '',
carrierId: carrierId:
item.customerId ||
item.partyBId || item.partyBId ||
item.partyBUserId || item.partyBUserId ||
item.contractPartyBId || item.contractPartyBId ||
item.customerContractPartyBId || item.customerContractPartyBId ||
item.carrierId || item.carrierId ||
'', '',
carrierPartyId: item.carrierPartyId || '',
carrierPartyRole: item.carrierPartyRole || '',
})) }))
.filter(item => item.carrierName); .filter(item => item.carrierName);
const current = const current =
contracts.find(item => String(item.id) === String(this.dialogForm.carrierContractId)) || this.taskCarrierOptions.find(
item =>
this.carrierOptionValue(item) === this.taskCarrierSelectedValue ||
(String(item.id) === String(this.dialogForm.carrierContractId) &&
(!this.dialogForm.carrierPartyId ||
String(item.carrierPartyId) === String(this.dialogForm.carrierPartyId)) &&
(!this.dialogForm.carrierName ||
item.carrierName === this.dialogForm.carrierName))
) ||
this.taskCarrierOptions.find(item => item.carrierName === this.dialogForm.carrierName); this.taskCarrierOptions.find(item => item.carrierName === this.dialogForm.carrierName);
if (current) { if (current) {
this.dialogForm.carrierContractId = current.id; this.dialogForm.carrierContractId = current.id;
this.dialogForm.carrierPartyId = current.carrierPartyId || '';
this.dialogForm.carrierPartyRole = current.carrierPartyRole || '';
this.dialogForm.carrierName = current.carrierName; this.dialogForm.carrierName = current.carrierName;
this.dialogForm.carrierId = current.carrierId || ''; this.dialogForm.carrierId = current.carrierId || '';
} else { } else {
this.dialogForm.carrierContractId = ''; this.dialogForm.carrierContractId = '';
this.dialogForm.carrierPartyId = '';
this.dialogForm.carrierPartyRole = '';
this.dialogForm.carrierName = ''; this.dialogForm.carrierName = '';
this.dialogForm.carrierId = ''; this.dialogForm.carrierId = '';
} }
@@ -4792,19 +4838,22 @@ export default {
.map(item => ({ .map(item => ({
...item, ...item,
carrierName: carrierName:
item.carrierName ||
item.partyB || item.partyB ||
item.partyBName || item.partyBName ||
item.contractPartyB || item.contractPartyB ||
item.customerContractPartyB || item.customerContractPartyB ||
item.carrierName ||
'', '',
carrierId: carrierId:
item.customerId ||
item.partyBId || item.partyBId ||
item.partyBUserId || item.partyBUserId ||
item.contractPartyBId || item.contractPartyBId ||
item.customerContractPartyBId || item.customerContractPartyBId ||
item.carrierId || item.carrierId ||
'', '',
carrierPartyId: item.carrierPartyId || '',
carrierPartyRole: item.carrierPartyRole || '',
})) }))
.filter(item => item.carrierName); .filter(item => item.carrierName);
const current = this.taskCarrierOptions.find( const current = this.taskCarrierOptions.find(
@@ -89,26 +89,26 @@
<el-col :span="8"> <el-col :span="8">
<el-form-item label="承运商合同" prop="carrierContractId"> <el-form-item label="承运商合同" prop="carrierContractId">
<el-select <el-select
v-model="form.carrierContractId" v-model="form.carrierOptionValue"
filterable filterable
clearable clearable
:disabled="readonly || !form.projectId" :disabled="readonly || !form.projectId"
style="width: 100%" style="width: 100%"
:placeholder="form.projectId ? '请选择承运商合同' : '请先选择项目'" :placeholder="form.projectId ? '请选择承运商合同客商' : '请先选择项目'"
@change="onCarrierContractChange" @change="onCarrierContractChange"
> >
<el-option <el-option
v-for="item in filteredCarrierContracts" v-for="item in filteredCarrierContractOptions"
:key="item.id" :key="item.optionValue"
:label="formatContractLabel(item)" :label="item.optionLabel"
:value="item.id" :value="item.optionValue"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="承运商"> <el-form-item label="承运商">
<el-input v-model="form.carrierName" disabled placeholder="选择承运商合同后自动填充" /> <el-input v-model="form.carrierName" disabled placeholder="选择承运商合同客商后自动填充" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
@@ -403,6 +403,9 @@ const createDefaultForm = () => ({
customerName: '', customerName: '',
carrierContractId: '', carrierContractId: '',
carrierContractName: '', carrierContractName: '',
carrierOptionValue: '',
carrierPartyId: '',
carrierPartyRole: '',
carrierName: '', carrierName: '',
waybillMode: 'new', waybillMode: 'new',
remark: '', remark: '',
@@ -442,6 +445,62 @@ const filteredCustomerContracts = computed(() => customerContracts.value);
const filteredCarrierContracts = computed(() => carrierContracts.value); const filteredCarrierContracts = computed(() => carrierContracts.value);
const parsePartyRows = (partyJson, fallbackName = '', partyRole = 'B') => {
let rows = [];
try {
const parsed = partyJson ? JSON.parse(partyJson) : [];
if (Array.isArray(parsed)) {
rows = parsed
.map(item => ({
id: item.id || '',
customerId: item.customerId || '',
customerName: String(item.customerName || '').trim(),
partyRole,
}))
.filter(item => item.customerName);
}
} catch (error) {
rows = [];
}
if (rows.length) return rows;
return String(fallbackName || '')
.split(/[、,,]/)
.map(name => name.trim())
.filter(Boolean)
.map(name => ({ id: '', customerId: '', customerName: name, partyRole }));
};
const filteredCarrierContractOptions = computed(() => {
const options = [];
filteredCarrierContracts.value.forEach(contract => {
const parties = [
...parsePartyRows(contract.partyBJson, contract.partyB, 'B'),
...parsePartyRows(contract.partyCJson, contract.partyC, 'C'),
];
if (!parties.length && contract.carrierName) {
parties.push({
id: '',
customerId: '',
customerName: contract.carrierName,
partyRole: 'B',
});
}
parties.forEach(party => {
const roleLabel = party.partyRole === 'C' ? '丙方' : '乙方';
const optionValue = [contract.id, party.id || '', party.customerName].join('__');
options.push({
...contract,
optionValue,
optionLabel: `${party.customerName}(${roleLabel}) - ${formatContractLabel(contract)}`,
carrierPartyId: party.id || '',
carrierPartyRole: party.partyRole,
carrierName: party.customerName,
});
});
});
return options;
});
const extractRecords = response => { const extractRecords = response => {
const data = response?.data || response; const data = response?.data || response;
if (Array.isArray(data)) return data; if (Array.isArray(data)) return data;
@@ -708,6 +767,10 @@ const normalizeContract = item => {
projectId: normalized.projectId, projectId: normalized.projectId,
partyA: normalized.partyA || '', partyA: normalized.partyA || '',
partyB: normalized.partyB || '', partyB: normalized.partyB || '',
partyC: normalized.partyC || '',
partyAJson: normalized.partyAJson || '',
partyBJson: normalized.partyBJson || '',
partyCJson: normalized.partyCJson || '',
customerName: normalized.customerName || normalized.partyA || '', customerName: normalized.customerName || normalized.partyA || '',
carrierName: normalized.carrierName || normalized.partyB || '', carrierName: normalized.carrierName || normalized.partyB || '',
}; };
@@ -972,10 +1035,9 @@ const autoSelectSingleContracts = () => {
form.customerContractId = customers[0].id; form.customerContractId = customers[0].id;
onCustomerContractChange(customers[0].id); onCustomerContractChange(customers[0].id);
} }
const carriers = filteredCarrierContracts.value; const carriers = filteredCarrierContractOptions.value;
if (carriers.length === 1 && !form.carrierContractId) { if (carriers.length === 1 && !form.carrierContractId) {
form.carrierContractId = carriers[0].id; onCarrierContractChange(carriers[0].optionValue);
onCarrierContractChange(carriers[0].id);
} }
}; };
@@ -987,6 +1049,9 @@ const onProjectChange = async id => {
form.customerName = ''; form.customerName = '';
form.carrierContractId = ''; form.carrierContractId = '';
form.carrierContractName = ''; form.carrierContractName = '';
form.carrierOptionValue = '';
form.carrierPartyId = '';
form.carrierPartyRole = '';
form.carrierName = ''; form.carrierName = '';
customerContracts.value = []; customerContracts.value = [];
carrierContracts.value = []; carrierContracts.value = [];
@@ -1005,9 +1070,15 @@ const onCustomerContractChange = id => {
form.customerName = item?.customerName || item?.partyA || ''; form.customerName = item?.customerName || item?.partyA || '';
}; };
const onCarrierContractChange = id => { const onCarrierContractChange = optionValue => {
const item = filteredCarrierContracts.value.find(row => String(row.id) === String(id)); const item = filteredCarrierContractOptions.value.find(
row => String(row.optionValue) === String(optionValue)
);
form.carrierOptionValue = item?.optionValue || '';
form.carrierContractId = item?.id || '';
form.carrierContractName = item?.contractName || ''; form.carrierContractName = item?.contractName || '';
form.carrierPartyId = item?.carrierPartyId || '';
form.carrierPartyRole = item?.carrierPartyRole || '';
form.carrierName = item?.carrierName || item?.partyB || ''; form.carrierName = item?.carrierName || item?.partyB || '';
}; };
@@ -2904,10 +2904,33 @@ export default {
buildRequestParams(params = {}) { buildRequestParams(params = {}) {
return this.settlementType ? { ...params, settlementType: this.settlementType } : params; return this.settlementType ? { ...params, settlementType: this.settlementType } : params;
}, },
syncBillingPlanOptions(contractId) { syncBillingPlanOptions(contractId, counterpartyName = '') {
const contracts = [...this.updateContractOptions, ...this.contractOptions]; const contracts = [...this.updateContractOptions, ...this.contractOptions];
const contract = contracts.find(item => String(item.id) === String(contractId)); const contract = contracts.find(item => String(item.id) === String(contractId));
const plans = this.parseJson(contract?.billingPlanJson); let plans = this.parseJson(contract?.billingPlanJson);
const targetName = String(
counterpartyName ||
this.updateFeeForm?.counterpartyName ||
this.generateQuery?.counterpartyName ||
this.currentDetail?.counterpartyName ||
this.currentDetail?.carrierName ||
this.currentDetail?.customerName ||
''
).trim();
if (targetName) {
const matched = plans.filter(
item =>
String(item.customerName || '').trim() === targetName ||
String(item.partyRowId || '') ===
String(this.updateFeeForm?.carrierPartyId || this.generateQuery?.carrierPartyId || '')
);
const hasBinding = plans.some(
item => item.customerName || item.customerId || item.partyRowId
);
if (matched.length || hasBinding) {
plans = matched;
}
}
this.billingPlanOptions = plans.map((item, index) => ({ this.billingPlanOptions = plans.map((item, index) => ({
id: id:
item.id || item.id ||
@@ -2916,11 +2939,16 @@ export default {
item.planName || item.planName ||
item.billingPlanName || item.billingPlanName ||
`plan-${index}`, `plan-${index}`,
name: item.name || item.planName || item.billingPlanName || `计费方案${index + 1}`, name: [
item.name || item.planName || item.billingPlanName || `计费方案${index + 1}`,
item.customerName ? `(${item.customerName})` : '',
].join(''),
defaultPlan: defaultPlan:
item.defaultPlan === true || item.defaultPlan === true ||
item.defaultPlan === 1 || item.defaultPlan === 1 ||
['true', '1'].includes(String(item.defaultPlan).toLowerCase()), ['true', '1'].includes(String(item.defaultPlan).toLowerCase()),
customerName: item.customerName || '',
partyRowId: item.partyRowId || '',
})); }));
return this.billingPlanOptions; return this.billingPlanOptions;
}, },