Merge remote-tracking branch 'origin/master'

This commit is contained in:
2026-08-21 11:07:39 +08:00
10 changed files with 409 additions and 74 deletions
@@ -233,6 +233,44 @@
</el-select>
</template>
<template #partyA-form>
<el-select
v-model="form.partyA"
class="business-crud-page__field"
placeholder="请选择甲方"
filterable
clearable
:loading="contractPartyLoading"
:disabled="dialogReadonly || !form.projectId"
>
<el-option
v-for="item in contractPartyAOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template #partyB-form>
<el-select
v-model="form.partyB"
class="business-crud-page__field"
placeholder="请选择乙方"
filterable
clearable
:loading="contractPartyLoading"
:disabled="dialogReadonly || !form.projectId"
>
<el-option
v-for="item in contractPartyBOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template #contractName-form>
<el-select
v-if="contractSelectEnabled"
@@ -680,22 +718,26 @@
</el-form-item>
</div>
<div
v-if="hasTransportType && isCarrierMode"
v-if="hasTransportType && taskCarrierRequired"
class="business-crud-page__subsection business-crud-page__carrier-section"
>
<div class="business-crud-page__subsection-head">
<span>承运信息</span>
</div>
<div class="business-crud-page__task-row">
<el-form-item label="承运商" required class="business-crud-page__task-span-1">
<el-form-item
:label="taskCarrierLabel"
required
class="business-crud-page__task-span-1"
>
<el-select
v-model="form.carrierName"
placeholder="请输入"
:placeholder="`请选择${taskCarrierLabel}`"
filterable
clearable
:suffix-icon="Search"
:loading="taskCarrierLoading"
:disabled="dialogReadonly"
:disabled="dialogReadonly || (isWaybillDetailLayout && !form.projectId)"
@visible-change="visible => visible && loadTaskCarrierOptions()"
@change="handleTaskCarrierChange"
>
@@ -1224,14 +1266,18 @@
</el-radio-group>
</el-form-item>
<template v-if="hasTransportType">
<el-form-item v-if="isCarrierMode" label="承运商" required>
<el-form-item v-if="taskCarrierRequired" :label="taskCarrierLabel" required>
<el-select
v-model="form.carrierName"
placeholder="请选择承运商"
:placeholder="`请选择${taskCarrierLabel}`"
filterable
clearable
:loading="taskCarrierLoading"
:disabled="dialogReadonly || form.carrierType !== '承运商'"
:disabled="
dialogReadonly ||
(isWaybillDetailLayout && !form.projectId) ||
!taskCarrierRequired
"
@visible-change="visible => visible && loadTaskCarrierOptions()"
@change="handleTaskCarrierChange"
>
@@ -4191,19 +4237,18 @@
<el-radio-button v-for="item in taskCarrierTypes" :key="item" :label="item" />
</el-radio-group>
</el-form-item>
<el-form-item v-if="dispatchItemForm.carrierType === '承运商'" label="承运商" required>
<el-form-item v-if="dispatchCarrierRequired" :label="dispatchCarrierLabel" required>
<el-select
v-model="dispatchItemForm.carrierName"
placeholder="请输入"
:placeholder="`请选择${dispatchCarrierLabel}`"
filterable
clearable
:suffix-icon="Search"
:loading="taskCarrierLoading"
:loading="dispatchCarrierLoading"
@visible-change="visible => visible && loadDispatchCarrierOptions()"
@change="handleDispatchCarrierChange"
>
<el-option
v-for="item in taskCarrierOptions"
v-for="item in dispatchCarrierOptions"
:key="
item.id || item.customerName || item.carrierName || item.fullName || item.name
"
@@ -4408,19 +4453,18 @@
<el-radio-button v-for="item in taskCarrierTypes" :key="item" :label="item" />
</el-radio-group>
</el-form-item>
<el-form-item v-if="dispatchItemForm.carrierType === '承运商'" label="承运商" required>
<el-form-item v-if="dispatchCarrierRequired" :label="dispatchCarrierLabel" required>
<el-select
v-model="dispatchItemForm.carrierName"
placeholder="请输入"
:placeholder="`请选择${dispatchCarrierLabel}`"
filterable
clearable
:suffix-icon="Search"
:loading="taskCarrierLoading"
:loading="dispatchCarrierLoading"
@visible-change="visible => visible && loadDispatchCarrierOptions()"
@change="handleDispatchCarrierChange"
>
<el-option
v-for="item in taskCarrierOptions"
v-for="item in dispatchCarrierOptions"
:key="
item.id || item.customerName || item.carrierName || item.fullName || item.name
"
@@ -5510,7 +5554,10 @@ import {
getDetail as getContractDetail,
getList as getContractList,
} from '@/api/business/contract-manage';
import { getList as getProjectList } from '@/api/business/project-apply';
import {
getDetail as getProjectDetail,
getList as getProjectList,
} from '@/api/business/project-apply';
import { getList as getLoadingList } from '@/api/business/loading-manage';
import { getList as getCommonRouteList } from '@/api/business/common-route';
import {
@@ -5927,6 +5974,10 @@ export default {
projectLoading: false,
contractOptions: [],
contractLoading: false,
contractPartyAOptions: [],
contractPartyBOptions: [],
contractPartyLoading: false,
contractPartyRequestId: 0,
shippingPlanOptions: [],
shippingPlanLoading: false,
selectedOrganizationId: '',
@@ -5942,6 +5993,9 @@ export default {
dispatchRow: {},
dispatchRows: [],
dispatchSaving: '',
dispatchCarrierOptions: [],
dispatchCarrierLoading: false,
dispatchCarrierRequestId: 0,
dispatchItemBox: false,
dispatchItemIndex: -1,
dispatchItemForm: defaultDispatchRow(),
@@ -6027,6 +6081,7 @@ export default {
taskCarrierTypes,
taskCarrierOptions: [],
taskCarrierLoading: false,
taskCarrierRequestId: 0,
taskDriverOptions: [],
taskDriverLoading: false,
taskCargoOptionsMap: {},
@@ -6388,6 +6443,12 @@ export default {
isCarrierMode() {
return this.form.carrierType === '承运商';
},
taskCarrierRequired() {
return this.isTaskCarrierRequired(this.form.carrierType);
},
taskCarrierLabel() {
return this.form.carrierType === '网货平台' ? '承运方' : '承运商';
},
showRoadEscortFields() {
return this.isRoadTransport && !this.isCarrierMode;
},
@@ -6535,6 +6596,12 @@ export default {
dispatchIsCarrierMode() {
return this.dispatchItemForm.carrierType === '承运商';
},
dispatchCarrierRequired() {
return this.isDispatchCarrierRequired(this.dispatchItemForm.carrierType);
},
dispatchCarrierLabel() {
return this.dispatchItemForm.carrierType === '网货平台' ? '承运方' : '承运商';
},
dispatchStationMode() {
return ['water', 'rail', 'air'].includes(this.dispatchTransportMode);
},
@@ -7794,6 +7861,101 @@ export default {
column.disabled = type !== 'add';
}
},
syncContractPartyDisabled() {
if (!this.isContractConfig) return;
const disabled = !this.form?.projectId;
['partyA', 'partyB'].forEach(prop => {
const column = this.findColumn?.(this.option.column, prop);
if (column) column.disabled = disabled;
});
},
clearContractPartyOptions() {
this.contractPartyRequestId += 1;
this.contractPartyLoading = false;
this.contractPartyAOptions = [];
this.contractPartyBOptions = [];
},
contractPartyNames(value, type = '') {
const names = [];
const append = item => {
if (item === undefined || item === null) return;
if (Array.isArray(item)) {
item.forEach(append);
return;
}
if (typeof item === 'object') {
append(
item[type === 'carrier' ? 'carrier' : 'customer'] ||
item.fullName ||
item.shortName ||
item.customerName ||
item.carrierName ||
item.name
);
return;
}
const text = String(item).trim();
if (!text) return;
try {
const parsed = JSON.parse(text);
if (parsed !== item) {
append(parsed);
return;
}
} catch (error) {
// 使
}
text
.split(/[、,;\n]/)
.map(name => name.trim())
.filter(Boolean)
.forEach(name => {
if (!names.includes(name)) names.push(name);
});
};
append(value);
return names;
},
contractPartyOptionsFromProject(project = {}, type = '') {
const names = this.contractPartyNames(
[
project[type === 'carrier' ? 'carrierJson' : 'customerJson'],
project[type === 'carrier' ? 'carrierNames' : 'customerNames'],
project[type === 'carrier' ? 'carrierName' : 'customerName'],
project[type === 'carrier' ? 'carriers' : 'customers'],
],
type
);
return names.map(name => ({ label: name, value: name }));
},
syncContractPartyOptions(project = {}, preserveCurrent = true) {
if (!this.isContractConfig) return;
this.contractPartyAOptions = this.contractPartyOptionsFromProject(project, 'customer');
this.contractPartyBOptions = this.contractPartyOptionsFromProject(project, 'carrier');
if (!preserveCurrent) {
this.form.partyA = '';
this.form.partyB = '';
}
},
loadContractPartyOptions(projectId, project = {}, preserveCurrent = true) {
if (!this.isContractConfig || !projectId) {
this.clearContractPartyOptions();
return Promise.resolve();
}
const requestId = ++this.contractPartyRequestId;
this.syncContractPartyOptions(project, preserveCurrent);
this.contractPartyLoading = true;
return getProjectDetail(projectId)
.then(res => {
if (requestId !== this.contractPartyRequestId) return;
const detail = res?.data?.data || {};
this.syncContractPartyOptions({ ...project, ...detail }, preserveCurrent);
})
.catch(() => {})
.finally(() => {
if (requestId === this.contractPartyRequestId) this.contractPartyLoading = false;
});
},
syncCustomCreateDialogButtons(type) {
if (
!this.config.enableContractCreateActions &&
@@ -7939,6 +8101,24 @@ export default {
}
return true;
},
isDefaultBillingPlan(plan) {
const value = plan?.defaultPlan;
return value === true || value === 1 || ['true', '1'].includes(String(value).toLowerCase());
},
validateBillingPlanDefault() {
const plans = Array.isArray(this.billingPlanRows) ? this.billingPlanRows : [];
if (!plans.length) return true;
const defaultCount = plans.filter(plan => this.isDefaultBillingPlan(plan)).length;
if (defaultCount === 0) {
this.$message.warning('请勾选一个默认计费方案');
return false;
}
if (defaultCount > 1) {
this.$message.warning('计费方案存在重复默认方案,请仅保留一个默认方案');
return false;
}
return true;
},
validateSettlementRule(source = this.settlementRuleForm, label = '') {
const rule = this.normalizeSettlementRule(source);
this.settlementRuleErrors = {};
@@ -8015,6 +8195,13 @@ export default {
submitRow.contractFileJson = JSON.stringify(this.contractFileRows);
}
if (this.config.enableBillingPlan) {
if (
this.isContractConfig &&
!skipValidation &&
!this.validateBillingPlanDefault()
) {
return false;
}
submitRow.billingPlanJson = JSON.stringify(this.billingPlanRows);
}
if (this.config.enableSettlementRule) {
@@ -8590,6 +8777,7 @@ export default {
return;
}
if (this.isContractFormPage) {
this.$router.$avueRouter?.closeTag?.();
this.$router.push('/business/contract-manage');
return;
}
@@ -8646,12 +8834,15 @@ export default {
this.syncCustomCreateDialogButtons(type);
this.dialogReadonly = type === 'view';
this.syncContractNoDisabled(type);
this.syncContractPartyDisabled();
if (type === 'add') {
this.form = {
...(this.config.defaultForm || {}),
};
this.syncContractPartyDisabled();
this.form.taskEntryMode = 'simple';
this.selectedProjectId = '';
this.clearContractPartyOptions();
this.selectedOrganizationId = '';
this.attachmentRows = [];
this.selectedAttachmentRows = [];
@@ -8679,6 +8870,9 @@ export default {
this.form.feeGenerationMode = this.form.feeGenerationMode || 'system';
this.changeRecordRows = [];
this.applyCurrentUserHandler();
this.taskCarrierRequestId += 1;
this.taskCarrierOptions = [];
this.taskCarrierLoading = false;
this.initTaskInfoForm();
this.initTransportPlanFormRows();
this.syncContractPeriod(false);
@@ -8715,6 +8909,14 @@ export default {
this.suppressTransportTypeClear = false;
});
this.selectedProjectId = this.form.projectId || '';
this.syncContractPartyDisabled();
this.loadContractPartyOptions(this.form.projectId, {
projectName: this.form.projectName,
customerNames: this.form.customerNames,
carrierNames: this.form.carrierNames,
customerJson: this.form.customerJson,
carrierJson: this.form.carrierJson,
});
this.loadProjectProcessConfigState();
this.selectedOrganizationId = this.form.organizationId || '';
this.attachmentRows = this.parseJsonArray(this.form.attachmentsJson);
@@ -8868,6 +9070,12 @@ export default {
this.form.projectFundLimit = '';
this.form.usedFundLimit = 0;
this.form.remainingFundLimit = 0;
if (this.isContractConfig) {
this.form.partyA = '';
this.form.partyB = '';
}
this.clearContractPartyOptions();
this.syncContractPartyDisabled();
if (this.contractSelectEnabled) {
this.form.contractId = '';
this.form.contractName = '';
@@ -8878,6 +9086,13 @@ export default {
this.shippingPlanOptions = [];
}
this.hasProjectProcessConfig = false;
if (this.isWaybillDetailLayout) {
this.taskCarrierRequestId += 1;
this.taskCarrierOptions = [];
this.taskCarrierLoading = false;
this.form.carrierName = '';
this.form.carrierId = '';
}
return;
}
this.form.projectId = project.id;
@@ -8887,6 +9102,17 @@ export default {
this.form.projectFundLimit = project.fundLimit || project.projectFundLimit || 0;
this.form.usedFundLimit = 0;
this.form.remainingFundLimit = this.calculateRemainingFundLimit();
if (this.isWaybillDetailLayout) {
this.form.carrierName = '';
this.form.carrierId = '';
this.loadTaskCarrierOptions(project);
}
if (this.isContractConfig) {
this.form.partyA = '';
this.form.partyB = '';
this.loadContractPartyOptions(project.id, project, false);
}
this.syncContractPartyDisabled();
if (this.contractSelectEnabled || this.config.enableTransportPlanForm) {
this.form.customerName =
project.customerName ||
@@ -8923,6 +9149,7 @@ export default {
return getContractList(1, 9999, {
projectId: this.form.projectId,
projectName: this.form.projectName,
...(this.config.contractQueryParams || {}),
})
.then(res => {
this.contractOptions = extractRecords(res);
@@ -9193,6 +9420,9 @@ export default {
if (this.isTaskFullMode) {
this.initTaskFullCargoRows(goodsRows);
}
if (this.isWaybillDetailLayout && this.form.projectId) {
this.loadTaskCarrierOptions();
}
},
handleTaskEntryModeChange(value) {
if (
@@ -9248,14 +9478,45 @@ export default {
this.form.trailerVehicleNo = '';
this.form.escortName = '';
this.form.escortPhone = '';
} else {
} else if (!this.isTaskCarrierRequired(nextValue)) {
this.form.carrierName = '';
this.form.carrierId = '';
}
this.form.carrierType = nextValue;
},
loadTaskCarrierOptions() {
isTaskCarrierRequired(carrierType) {
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
},
loadTaskCarrierOptions(project = {}) {
if (!this.taskInfoFormEnabled || this.taskCarrierLoading) return Promise.resolve([]);
if (this.isWaybillDetailLayout) {
const projectId = project.id || project.projectId || this.form.projectId;
const requestId = ++this.taskCarrierRequestId;
const currentProject = this.projectOptions.find(
item => String(item.id) === String(projectId)
);
this.taskCarrierOptions = this.getProjectCarrierOptions({
...currentProject,
...project,
});
if (!projectId) return Promise.resolve(this.taskCarrierOptions);
this.taskCarrierLoading = true;
return getProjectDetail(projectId)
.then(res => {
if (requestId !== this.taskCarrierRequestId) return this.taskCarrierOptions;
const detail = res?.data?.data || {};
this.taskCarrierOptions = this.getProjectCarrierOptions({
...currentProject,
...project,
...detail,
});
return this.taskCarrierOptions;
})
.catch(() => this.taskCarrierOptions)
.finally(() => {
if (requestId === this.taskCarrierRequestId) this.taskCarrierLoading = false;
});
}
this.taskCarrierLoading = true;
return getCarrierCustomerList(1, 9999, { customerType: '承运商', status: 1 })
.then(res => {
@@ -9611,8 +9872,8 @@ export default {
['carrierType', '承运类型'],
['vehicleNo', this.transportVehicleNoLabel],
];
if (row.carrierType === '承运商') {
requiredFields.push(['carrierName', '承运商']);
if (this.isTaskCarrierRequired(row.carrierType)) {
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
}
if (this.isRoadTransport && row.carrierType === '自运') {
requiredFields.push(['driverName', '司机']);
@@ -11961,7 +12222,11 @@ export default {
this.transportPlanImportContractOptions = [];
if (!projectId) return;
this.transportPlanImportContractLoading = true;
getContractList(1, 9999, { projectId, projectName: project?.projectName })
getContractList(1, 9999, {
projectId,
projectName: project?.projectName,
...(this.config.contractQueryParams || {}),
})
.then(res => {
this.transportPlanImportContractOptions = extractRecords(res);
const contract = this.transportPlanImportContractOptions[0];
@@ -12335,6 +12600,8 @@ export default {
return;
}
this.dispatchRow = { ...row };
this.dispatchCarrierOptions = [];
this.dispatchCarrierRequestId += 1;
this.dispatchBox = true;
this.dispatchLoading = true;
this.dispatchRows = [];
@@ -12351,6 +12618,7 @@ export default {
};
this.dispatchRow = detail;
this.dispatchRows = this.buildDispatchRows(detail);
this.loadDispatchCarrierOptions(detail);
})
.finally(() => {
this.dispatchLoading = false;
@@ -12384,6 +12652,9 @@ export default {
this.dispatchRows = [];
this.dispatchLoading = false;
this.dispatchSaving = '';
this.dispatchCarrierRequestId += 1;
this.dispatchCarrierOptions = [];
this.dispatchCarrierLoading = false;
this.dispatchItemBox = false;
this.dispatchItemIndex = -1;
this.dispatchItemForm = defaultDispatchRow();
@@ -12982,30 +13253,72 @@ export default {
this.dispatchItemForm.trailerVehicleNo = '';
this.dispatchItemForm.escortName = '';
this.dispatchItemForm.escortPhone = '';
} else {
} else if (!this.isDispatchCarrierRequired(nextValue)) {
this.dispatchItemForm.carrierId = '';
this.dispatchItemForm.carrierName = '';
}
this.dispatchItemForm.carrierType = nextValue;
},
loadDispatchCarrierOptions() {
if (this.taskCarrierLoading) return Promise.resolve([]);
this.taskCarrierLoading = true;
return getCarrierCustomerList(1, 9999, { customerType: '承运商', status: 1 })
isDispatchCarrierRequired(carrierType) {
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
},
getProjectCarrierOptions(project = {}) {
const rows = this.parseJsonArray(project.carrierJson);
const names = String(project.carrierNames || '')
.split(/[、,;\n]/)
.map(item => item.trim())
.filter(Boolean);
const options = [];
const append = item => {
if (!item) return;
const id = item.id || item.carrierId || item.customerId || '';
const label =
(typeof item === 'string' ? item : '') ||
item.carrier ||
item.carrierName ||
item.fullName ||
item.shortName ||
item.customerName ||
item.name ||
'';
const value = String(label).trim();
if (!value || options.some(option => option.value === value)) return;
options.push({ id, carrierName: value, fullName: value, value });
};
rows.forEach(append);
names.forEach(append);
return options;
},
loadDispatchCarrierOptions(plan = this.dispatchRow) {
if (this.dispatchCarrierLoading) return Promise.resolve(this.dispatchCarrierOptions);
const projectId = plan?.projectId;
const requestId = ++this.dispatchCarrierRequestId;
const project = this.projectOptions.find(
item => String(item.id) === String(projectId)
);
this.dispatchCarrierOptions = this.getProjectCarrierOptions({ ...project, ...plan });
if (!projectId) return Promise.resolve(this.dispatchCarrierOptions);
this.dispatchCarrierLoading = true;
return getProjectDetail(projectId)
.then(res => {
this.taskCarrierOptions = extractRecords(res);
return this.taskCarrierOptions;
if (requestId !== this.dispatchCarrierRequestId) return this.dispatchCarrierOptions;
const detail = res?.data?.data || {};
this.dispatchCarrierOptions = this.getProjectCarrierOptions({
...project,
...plan,
...detail,
});
return this.dispatchCarrierOptions;
})
.catch(() => this.dispatchCarrierOptions)
.finally(() => {
this.taskCarrierLoading = false;
if (requestId === this.dispatchCarrierRequestId) {
this.dispatchCarrierLoading = false;
}
});
},
handleDispatchCarrierChange(value) {
const carrier = this.taskCarrierOptions.find(item =>
[item.customerName, item.carrierName, item.fullName, item.name].some(
name => String(name || '') === String(value || '')
)
);
const carrier = this.dispatchCarrierOptions.find(item => item.value === String(value || ''));
this.dispatchItemForm.carrierId = carrier?.id || '';
this.dispatchItemForm.carrierName = value || '';
},
@@ -13211,8 +13524,8 @@ export default {
: '车牌号',
],
];
if (row.carrierType === '承运商') {
requiredFields.push(['carrierName', '承运商']);
if (this.isDispatchCarrierRequired(row.carrierType)) {
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
}
const fullEntry = row.taskEntryMode === 'full';
const roadTransport = this.resolveTransportMode(row.transportType) === 'road';