调整业务模块

This commit is contained in:
2026-09-04 05:23:34 +08:00
parent c63c7c3e33
commit e789f1647a
10 changed files with 2082 additions and 591 deletions
@@ -34,7 +34,7 @@
:model-value="form.masterNo || '系统自动生成'"
readonly /></el-form-item></el-col
><el-col :span="6"
><el-form-item label="运输组织类型" required
><el-form-item label="运输类型" required
><el-select v-model="form.transportOrganizationType" disabled
><el-option label="多式联运" value="多式联运" /></el-select></el-form-item></el-col
><el-col :span="24"
@@ -54,75 +54,77 @@
<el-link type="primary" @click="openRouteDialog">选择线路</el-link>
</div>
</div>
<el-form :model="form" label-position="right" label-width="auto"
><div class="route-steps">
<el-steps direction="vertical" :active="form.routes.length + 1">
<el-step>
<template #icon><span class="route-step-icon route-step-icon--start"></span></template>
<template #description>
<div class="route-node-form">
<el-form-item label="发货地址" required class="route-address-form-item"
<el-form class="route-info-form" :model="form" label-position="right" label-width="96px"
><div class="route-flow">
<div class="route-flow-row">
<span class="route-flow-marker route-flow-marker--start"></span>
<div class="route-flow-body">
<div class="route-node-form">
<el-form-item label="发货地" required class="route-address-form-item"
><div class="route-address-inputs"
><el-cascader v-model="addressRegionPaths.departure" class="route-address-name" :options="regionOptions" :props="regionCascaderProps" :loading="regionLoading" placeholder="请选择省市区" clearable filterable @visible-change="visible => visible && loadRegionOptions()" @change="value => handleAddressRegionChange('departure', value)" /><el-input v-model="form.departureAddress" class="route-address-detail" readonly placeholder="请输入详细地址" @click="openAddressMap('departure')" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="OfficeBuilding" @click="openCommonAddress('departure')" /></el-tooltip></div
><el-input v-if="isNonRoadAddress('departure')" v-model="form.departureName" class="route-address-name" readonly placeholder="请选择站点" /><el-cascader v-else v-model="addressRegionPaths.departure" class="route-address-name" :options="regionOptions" :props="regionCascaderProps" :loading="regionLoading" placeholder="请选择省市区" clearable filterable @visible-change="visible => visible && loadRegionOptions()" @change="value => handleAddressRegionChange('departure', value)" /><el-input v-model="form.departureAddress" class="route-address-detail" readonly placeholder="请输入详细地址" @click="handleAddressDetailClick('departure')" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="OfficeBuilding" @click="openCommonAddress('departure')" /></el-tooltip></div
></el-form-item>
<el-form-item label="联系人"
<el-form-item label="联系人" class="route-contact-form-item"
><el-input v-model="form.departureContact" placeholder="请输入"
/></el-form-item>
<el-form-item label="联系方式"
<el-form-item label="联系方式" class="route-phone-form-item"
><el-input v-model="form.departurePhone" placeholder="请输入"
/></el-form-item>
<el-form-item label="计划开始日期"
<el-form-item label="计划开始" class="route-date-form-item"
><el-date-picker
v-model="form.planStartTime"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择"
style="width: 200px"
/></el-form-item>
</div>
</template>
</el-step>
<el-step v-for="(route, index) in form.routes" :key="index">
<template #icon><span class="route-step-icon route-step-icon--middle"></span></template>
<template #title><div class="route-step-title"><span class="route-segment-label">{{ index + 1 }}</span><el-tooltip :content="getRouteTitle(index).replace(`段${index + 1}`, '')" placement="top"><span class="route-title-path">{{ formatRouteTitle(getRouteTitle(index), index + 1) }}</span></el-tooltip><div class="route-transport-control"><span>运输方式</span><el-select v-model="route.transportType" placeholder="请选择"><el-option v-for="type in transports" :key="type" :label="type" :value="type" /></el-select></div></div></template>
<template #description>
</div>
</div>
</div>
<template v-for="(route, index) in form.routes" :key="index">
<div class="route-flow-segment">
<div class="route-step-title"><span class="route-segment-label">{{ circledSegmentNumber(index + 1) }} {{ index + 1 }}</span><el-tooltip :content="getRouteTitle(index).replace(`段${index + 1}`, '')" placement="top"><span class="route-title-path">{{ formatRouteTitle(getRouteTitle(index), index + 1) }}</span></el-tooltip><div class="route-transport-control"><span>运输方式<span class="required-asterisk">*</span></span><el-select v-model="route.transportType" placeholder="请选择"><el-option v-for="type in transports" :key="type" :label="type" :value="type" /></el-select></div></div>
</div>
<div class="route-flow-row">
<span class="route-flow-marker route-flow-marker--middle"></span>
<div class="route-flow-body">
<div class="route-node-form">
<el-form-item label="途经地" required class="route-address-form-item"
><div class="route-address-inputs"
><el-cascader v-model="addressRegionPaths[`route-${index}`]" class="route-address-name" :options="regionOptions" :props="regionCascaderProps" :loading="regionLoading" :disabled="!route.transportType" placeholder="请选择省市区" clearable filterable @visible-change="visible => visible && loadRegionOptions()" @change="value => handleAddressRegionChange(`route-${index}`, value)" /><el-input v-model="route.departureAddress" class="route-address-detail" readonly :disabled="!route.transportType || !isRoadTransportType(route.transportType)" placeholder="请先选择运输方式" @click="openAddressMap(`route-${index}`)" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="OfficeBuilding" :disabled="!route.transportType" @click="openCommonAddress(`route-${index}`)" /></el-tooltip></div
><el-input v-if="isNonRoadAddress(`route-${index}`)" v-model="route.departureName" class="route-address-name" readonly :disabled="!route.transportType" placeholder="请先选择运输方式" /><el-cascader v-else v-model="addressRegionPaths[`route-${index}`]" class="route-address-name" :options="regionOptions" :props="regionCascaderProps" :loading="regionLoading" :disabled="!route.transportType" placeholder="请选择省市区" clearable filterable @visible-change="visible => visible && loadRegionOptions()" @change="value => handleAddressRegionChange(`route-${index}`, value)" /><el-input v-model="route.departureAddress" class="route-address-detail" readonly :disabled="!route.transportType" placeholder="请先选择运输方式" @click="handleAddressDetailClick(`route-${index}`)" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="OfficeBuilding" :disabled="!route.transportType" @click="openCommonAddress(`route-${index}`)" /></el-tooltip></div
></el-form-item>
<el-form-item label="联系人"><el-input v-model="route.departureContact" placeholder="请输入" /></el-form-item>
<el-form-item label="联系方式"><div class="route-contact-actions"><el-input v-model="route.departurePhone" placeholder="请输入" /><el-tooltip v-if="index === form.routes.length - 1" content="新增途经点" placement="top"><el-button class="route-action-button" type="primary" link @click="addRoute"><el-icon><CirclePlus /></el-icon></el-button></el-tooltip><el-tooltip content="删除途经点" placement="top"><el-button class="route-action-button" type="danger" link @click="removeRoute(index)"><el-icon><Remove /></el-icon></el-button></el-tooltip></div></el-form-item>
<el-form-item label="联系人" class="route-contact-form-item"><el-input v-model="route.departureContact" placeholder="请输入" /></el-form-item>
<el-form-item label="联系方式" class="route-phone-form-item"><div class="route-contact-actions"><el-input v-model="route.departurePhone" placeholder="请输入" /><el-tooltip v-if="index === form.routes.length - 1" content="新增途经点" placement="top"><el-button class="route-action-button" type="primary" link @click="addRoute"><el-icon><CirclePlus /></el-icon></el-button></el-tooltip><el-tooltip content="删除途经点" placement="top"><el-button class="route-action-button" type="danger" link @click="removeRoute(index)"><el-icon><Remove /></el-icon></el-button></el-tooltip></div></el-form-item>
</div>
</template>
</el-step>
<el-step>
<template #icon><span class="route-step-icon route-step-icon--end"></span></template>
<template #title><div class="route-step-title"><span class="route-segment-label">{{ form.routes.length + 1 }}</span><el-tooltip :content="getFinalRouteTitle().replace(`段${form.routes.length + 1}`, '')" placement="top"><span class="route-title-path">{{ formatRouteTitle(getFinalRouteTitle(), form.routes.length + 1) }}</span></el-tooltip><div class="route-transport-control"><span>运输方式</span><el-select v-model="form.finalTransportType" placeholder="请选择"><el-option v-for="type in transports" :key="type" :label="type" :value="type" /></el-select></div></div></template>
<template #description>
<div class="route-node-form">
<el-form-item label="收货地址" required class="route-address-form-item"
</div>
</div>
</template>
<div class="route-flow-segment">
<div class="route-step-title"><span class="route-segment-label">{{ circledSegmentNumber(form.routes.length + 1) }} {{ form.routes.length + 1 }}</span><el-tooltip :content="getFinalRouteTitle().replace(`段${form.routes.length + 1}`, '')" placement="top"><span class="route-title-path">{{ formatRouteTitle(getFinalRouteTitle(), form.routes.length + 1) }}</span></el-tooltip><div class="route-transport-control"><span>运输方式<span class="required-asterisk">*</span></span><el-select v-model="form.finalTransportType" placeholder="请选择"><el-option v-for="type in transports" :key="type" :label="type" :value="type" /></el-select></div></div>
</div>
<div class="route-flow-row">
<span class="route-flow-marker route-flow-marker--end"></span>
<div class="route-flow-body">
<div class="route-node-form">
<el-form-item label="收货地" required class="route-address-form-item"
><div class="route-address-inputs"
><el-cascader v-model="addressRegionPaths.arrival" class="route-address-name" :options="regionOptions" :props="regionCascaderProps" :loading="regionLoading" :disabled="!form.finalTransportType" placeholder="请选择省市区" clearable filterable @visible-change="visible => visible && loadRegionOptions()" @change="value => handleAddressRegionChange('arrival', value)" /><el-input v-model="form.arrivalAddress" class="route-address-detail" readonly :disabled="!form.finalTransportType || !isRoadTransportType(form.finalTransportType)" placeholder="请选择省市区" @click="openAddressMap('arrival')" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="OfficeBuilding" :disabled="!form.finalTransportType" @click="openCommonAddress('arrival')" /></el-tooltip></div
><el-input v-if="isNonRoadAddress('arrival')" v-model="form.arrivalName" class="route-address-name" readonly :disabled="!form.finalTransportType" placeholder="请先选择运输方式" /><el-cascader v-else v-model="addressRegionPaths.arrival" class="route-address-name" :options="regionOptions" :props="regionCascaderProps" :loading="regionLoading" :disabled="!form.finalTransportType" placeholder="请选择省市区" clearable filterable @visible-change="visible => visible && loadRegionOptions()" @change="value => handleAddressRegionChange('arrival', value)" /><el-input v-model="form.arrivalAddress" class="route-address-detail" readonly :disabled="!form.finalTransportType" placeholder="请选择运输方式" @click="handleAddressDetailClick('arrival')" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="OfficeBuilding" :disabled="!form.finalTransportType" @click="openCommonAddress('arrival')" /></el-tooltip></div
></el-form-item>
<el-form-item label="联系人"
<el-form-item label="联系人" class="route-contact-form-item"
><el-input v-model="form.arrivalContact" placeholder="请输入" /></el-form-item>
<el-form-item label="联系方式"
<el-form-item label="联系方式" class="route-phone-form-item"
><el-input v-model="form.arrivalPhone" placeholder="请输入" /></el-form-item>
<el-form-item label="计划结束日期"
<el-form-item label="计划结束" class="route-date-form-item"
><el-date-picker
v-model="form.planEndTime"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择"
style="width: 200px"
/></el-form-item>
</div>
</template>
</el-step>
</el-steps>
</div>
</div>
</div>
</div></el-form
>
</section>
@@ -157,7 +159,8 @@
</template></el-autocomplete
></template></el-table-column
><el-table-column label="货物类型" min-width="130"
><template #default="{ row }"
><template #header><span>货物类型<span class="required-asterisk">*</span></span></template>
<template #default="{ row }"
><el-cascader
v-model="row.cargoTypePath"
:options="cargoTypeOptions"
@@ -170,14 +173,16 @@
@change="value => handleCargoTypeChange(row, value)"
/></template></el-table-column
><el-table-column label="数量" width="180" class-name="master-editor__quantity-cell"
><template #default="{ row }"
><template #header><span>数量<span class="required-asterisk">*</span></span></template>
<template #default="{ row }"
><el-input
v-model="row.quantity"
inputmode="decimal"
placeholder="请输入"
@input="value => handleQuantityInput(row, value)" /></template></el-table-column
><el-table-column label="数量单位" width="120"
><template #default="{ row }"
><template #header><span>数量单位<span class="required-asterisk">*</span></span></template>
<template #default="{ row }"
><el-select v-model="row.quantityUnit"
><el-option v-for="unit in quantityUnitOptions" :key="unit" :label="unit" :value="unit" /></el-select></template></el-table-column
><el-table-column label="包装" width="130"
@@ -194,6 +199,15 @@
><el-table-column label="型号" min-width="120"
><template #default="{ row }"
><el-input v-model="row.model" placeholder="请输入" /></template></el-table-column
><el-table-column label="物料编码" min-width="140"
><template #default="{ row }"
><el-input v-model="row.materialCode" placeholder="请输入" /></template></el-table-column
><el-table-column label="设备编码" min-width="140"
><template #default="{ row }"
><el-input v-model="row.deviceCode" placeholder="请输入" /></template></el-table-column
><el-table-column label="备注" min-width="180"
><template #default="{ row }"
><el-input v-model="row.remark" maxlength="200" show-word-limit placeholder="请输入" /></template></el-table-column
><el-table-column label="操作" width="140"
><template #default="{ $index }"
><div class="goods-actions"
@@ -717,6 +731,11 @@ export default {
responseData(response) {
return response?.data?.data || response?.data || response || {};
},
circledSegmentNumber(number) {
const value = Number(number);
const circledNumbers = '①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳';
return circledNumbers[value - 1] || String(value);
},
async loadRegionOptions() {
if (this.regionOptions.length) return this.regionOptions;
if (this.regionRequest) return this.regionRequest;
@@ -767,9 +786,19 @@ export default {
},
extractChinaRegionOptions(regions = []) {
const china = regions.find(item => ['中国', '中华人民共和国'].includes(item.title));
if (china?.children?.length) return china.children;
if (regions.length === 1 && regions[0].children?.length) return regions[0].children;
return regions;
const chinaChildren = china?.children || [];
// 部分行政区划数据的省级节点 parentId 为 0,另一部分挂在“中国(+86)”下,需合并两类根节点。
const rootRegions = regions.filter(
item => item !== china && ['0', '', 'null', 'undefined'].includes(String(item.parentId ?? ''))
);
const merged = [...chinaChildren, ...rootRegions];
const seen = new Set();
return merged.filter(item => {
const key = String(item.id ?? item.value ?? item.title ?? '');
if (seen.has(key)) return false;
seen.add(key);
return true;
});
},
findRegionPathByName(options = [], regionName = '', parents = []) {
const target = String(regionName || '').replace(/\s+/g, '');
@@ -805,6 +834,32 @@ export default {
});
return labels;
},
setAddressRegionNames(field, labels = []) {
if (!field) return;
const normalized = labels.map(label => String(label || '').trim()).filter(Boolean);
const province = normalized.find(label => /省|自治区|特别行政区/.test(label)) || normalized[0] || '';
const city = normalized.filter(label => /市|州|盟/.test(label)).at(-1) || '';
const district = normalized.filter(label => /区|县|旗/.test(label)).at(-1) || '';
if (field.provinceName) field.model[field.provinceName] = province;
if (field.cityName) field.model[field.cityName] = city;
if (field.districtName) field.model[field.districtName] = district;
},
setAddressRegionNamesFromRow(field, row = {}) {
const labels = [row.provinceName, row.cityName, row.districtName].filter(Boolean);
if (labels.length) {
this.setAddressRegionNames(field, labels);
return;
}
const text = String(row.regionName || '').replace(/\s+/g, '');
if (!text) return;
const provinceMatch = text.match(/^.*?(?:省|自治区|特别行政区)/);
const rest = provinceMatch ? text.slice(provinceMatch[0].length) : text;
const cityMatch = rest.match(/.+?(?:市|州|盟)/);
const districtMatch = cityMatch
? rest.slice(cityMatch[0].length).match(/^[^市州盟]*?(?:区|县|旗)/)
: rest.match(/.+?(?:区|县|旗)/);
this.setAddressRegionNames(field, [provinceMatch?.[0], cityMatch?.[0], districtMatch?.[0]]);
},
handleAddressRegionChange(target, value) {
const field = this.addressModel(target);
const path = Array.isArray(value) ? value.map(item => String(item)) : [];
@@ -813,6 +868,8 @@ export default {
const labels = this.regionLabels(path);
field.model[field.name] = labels.join('');
field.model[field.regionCode] = path.at(-1) || '';
field.model[field.siteCode] = '';
this.setAddressRegionNames(field, labels);
},
syncAddressRegionPath(target) {
const field = this.addressModel(target);
@@ -822,6 +879,7 @@ export default {
? codePath
: this.findRegionPathByName(this.regionOptions, field.model[field.name]);
this.addressRegionPaths = { ...this.addressRegionPaths, [target]: path };
if (path.length) this.setAddressRegionNames(field, this.regionLabels(path));
},
syncAddressRegionPaths() {
if (!this.regionOptions.length) return;
@@ -880,19 +938,27 @@ export default {
const date = this.normalizeDateValue(value);
return date ? `${date} ${endOfDay ? '23:59:59' : '00:00:00'}` : '';
},
getRegionDisplay(target, fallback) {
getRoadRegionDisplay(target, fallback) {
const labels = this.regionLabels(this.addressRegionPaths[target] || []);
return labels.length ? labels.join('/') : fallback;
return labels.at(-1) || fallback;
},
getAddressDisplay(target, fallback) {
const field = this.addressModel(target);
const name = field?.model?.[field.name] || fallback;
return this.isNonRoadAddress(target) ? name : this.getRoadRegionDisplay(target, name);
},
getRouteTitle(index) {
const departureName =
index === 0
? this.getRegionDisplay('departure', this.form.departureName || '发货地址')
: this.getRegionDisplay(
? this.getAddressDisplay(
'departure',
this.form.departureName || '发货地址'
)
: this.getAddressDisplay(
`route-${index - 1}`,
this.form.routes[index - 1].departureName || '途经地'
);
const arrivalName = this.getRegionDisplay(
const arrivalName = this.getAddressDisplay(
`route-${index}`,
this.form.routes[index].departureName || '途经地'
);
@@ -912,11 +978,14 @@ export default {
return `${this.truncateRouteAddress(departure)}${separator}${this.truncateRouteAddress(arrival)}`;
},
getFinalRouteTitle() {
const departureName = this.getRegionDisplay(
const departureName = this.getAddressDisplay(
`route-${this.form.routes.length - 1}`,
this.lastRouteName
);
const arrivalName = this.getRegionDisplay('arrival', this.form.arrivalName || '收货地址');
const arrivalName = this.getAddressDisplay(
'arrival',
this.form.arrivalName || '收货地址'
);
return `段${this.form.routes.length + 1}${departureName} → ${arrivalName}`;
},
responseRecords(response) {
@@ -965,6 +1034,9 @@ export default {
latitude: 'departureLatitude',
regionCode: 'departureRegionCode',
siteCode: 'departureSiteCode',
provinceName: 'departureProvinceName',
cityName: 'departureCityName',
districtName: 'departureDistrictName',
}
: null;
}
@@ -979,13 +1051,16 @@ export default {
latitude: `${prefix}Latitude`,
regionCode: `${prefix}RegionCode`,
siteCode: `${prefix}SiteCode`,
provinceName: `${prefix}ProvinceName`,
cityName: `${prefix}CityName`,
districtName: `${prefix}DistrictName`,
};
},
addressTransportType(target) {
if (target.startsWith('route-')) {
return this.form.routes[Number(target.slice(6))]?.transportType || '';
}
return target === 'arrival' ? this.form.finalTransportType : '';
return target === 'arrival' ? this.form.finalTransportType : this.form.routes[0]?.transportType || '';
},
addressTransportMode(target) {
const type = this.addressTransportType(target);
@@ -998,6 +1073,17 @@ export default {
const value = String(type || '').trim().toLowerCase();
return value === 'road' || value.includes('公路');
},
isNonRoadAddress(target) {
const field = this.addressModel(target);
if (!field) return false;
const siteCode = String(field.model[field.siteCode] || '').trim();
// 地址展示模式由实际选择的数据决定:有站点编码表示非公路站点名称,
// 没有站点编码则保留省市区级联,避免切换运输方式时改写已有地址。
return Boolean(siteCode && siteCode !== '/');
},
resolveAddressRegionCode(row = {}) {
return row.regionCode || row.districtCode || row.areaCode || row.adcode || '';
},
openCommonAddress(target) {
this.addressTarget = target;
if (this.addressTransportMode(target) !== 'road') {
@@ -1054,14 +1140,15 @@ export default {
const target = this.addressModel(this.addressTarget);
if (!target) return;
const station = this.normalizeStation(row);
target.model[target.name] = station.regionName || station.stationName;
target.model[target.name] = station.stationName || station.regionName;
target.model[target.address] = station.detailAddress;
target.model[target.longitude] = station.longitude || '';
target.model[target.latitude] = station.latitude || '';
target.model[target.regionCode] = station.regionCode || '';
target.model[target.regionCode] = this.resolveAddressRegionCode(station);
target.model[target.siteCode] = station.stationCode;
this.setAddressRegionNamesFromRow(target, station);
this.stationDialogVisible = false;
this.syncAddressRegionPath(this.addressTarget);
this.loadRegionOptions().then(() => this.syncAddressRegionPath(this.addressTarget));
},
async loadAddressList() {
this.addressLoading = true;
@@ -1096,12 +1183,21 @@ export default {
target.model[target.phone] = row.contactPhone || target.model[target.phone] || '';
target.model[target.longitude] = row.longitude || '';
target.model[target.latitude] = row.latitude || '';
target.model[target.regionCode] = row.regionCode || '';
target.model[target.regionCode] = this.resolveAddressRegionCode(row);
target.model[target.siteCode] = '';
this.setAddressRegionNamesFromRow(target, row);
this.addressDialogVisible = false;
this.syncAddressRegionPath(this.addressTarget);
this.loadRegionOptions().then(() => this.syncAddressRegionPath(this.addressTarget));
},
handleAddressDetailClick(target) {
if (this.addressTransportMode(target) !== 'road') {
this.openCommonAddress(target);
return;
}
this.openAddressMap(target);
},
openAddressMap(target) {
if ((target.startsWith('route-') || target === 'arrival') && !this.isRoadTransportType(this.addressTransportType(target))) {
if (this.addressTransportMode(target) !== 'road') {
this.$message.info('非公路运输地址不能使用地图选择');
return;
}
@@ -1203,6 +1299,9 @@ export default {
const component = result.regeocode?.addressComponent || {};
this.mapSelected.detailAddress =
result.regeocode?.formattedAddress || this.mapSelected.detailAddress;
this.mapSelected.provinceName = component.province || '';
this.mapSelected.cityName = component.city || '';
this.mapSelected.districtName = component.district || '';
this.mapSelected.regionName = [component.province, component.city, component.district]
.filter(Boolean)
.join('');
@@ -1228,6 +1327,8 @@ export default {
target.model[target.longitude] = this.mapSelected.longitude;
target.model[target.latitude] = this.mapSelected.latitude;
target.model[target.regionCode] = this.mapSelected.regionCode || '';
target.model[target.siteCode] = '';
this.setAddressRegionNamesFromRow(target, this.mapSelected);
this.mapDialogVisible = false;
this.syncAddressRegionPath(this.mapTarget);
},
@@ -1445,6 +1546,8 @@ export default {
'brand',
'specification',
'model',
'materialCode',
'deviceCode',
'remark',
].some(key => firstGoods[key]);
@@ -1489,6 +1592,8 @@ export default {
brand: row.brand || '',
specification: row.specification || row.spec || '',
model: row.model || '',
materialCode: row.materialCode || '',
deviceCode: row.deviceCode || '',
remark: row.remark || row.descriptionOne || '',
};
},
@@ -1590,7 +1695,12 @@ export default {
this.syncAddressRegionPaths();
},
addGoods(index) {
this.form.goods.splice(index + 1, 0, { quantityUnit: quantityUnitOptions[0] });
this.form.goods.splice(index + 1, 0, {
quantityUnit: quantityUnitOptions[0],
materialCode: '',
deviceCode: '',
remark: '',
});
},
removeGoods(index) {
if (this.form.goods.length > 1) this.form.goods.splice(index, 1);
@@ -1707,13 +1817,20 @@ export default {
await this.$refs.form.validate();
if (!this.form.departureAddress || !this.form.arrivalAddress)
return this.$message.warning('请填写收发货地址');
if (!this.form.goods.some(item => item.cargoType && item.quantity > 0 && item.quantityUnit))
return this.$message.warning('请至少填写一条有效货物');
if (
this.form.routes.some(item => !item.departureName || !item.transportType) ||
!this.form.finalTransportType
)
return this.$message.warning('请完整填写途经地及运输方式');
const invalidGoodsIndex = this.form.goods.findIndex(
item =>
!String(item.cargoType || '').trim() ||
!(Number(item.quantity) > 0) ||
!String(item.quantityUnit || '').trim()
);
if (invalidGoodsIndex >= 0)
return this.$message.warning(`请完整填写第${invalidGoodsIndex + 1}条货物的类型、数量和数量单位`);
const invalidRouteIndex = this.form.routes.findIndex(
item => !item.departureName || !item.transportType
);
if (invalidRouteIndex >= 0)
return this.$message.warning(`请完整填写第${invalidRouteIndex + 1}段途经地及运输方式`);
if (!this.form.finalTransportType) return this.$message.warning('请填写最后一段运输方式');
}
const payload = {
...this.form,
@@ -1721,7 +1838,17 @@ export default {
planEndTime: this.normalizeDateTimeValue(this.form.planEndTime, true),
attachmentsJson: JSON.stringify(this.attachmentRows),
routes: [
...this.form.routes.map((route, index) => ({ ...route, segmentNo: `段${index + 1}` })),
...this.form.routes.map((route, index) => ({
...route,
segmentNo: `段${index + 1}`,
...(index === 0
? {
departureProvinceName: this.form.departureProvinceName,
departureCityName: this.form.departureCityName,
departureDistrictName: this.form.departureDistrictName,
}
: {}),
})),
{
segmentNo: `段${this.form.routes.length + 1}`,
departureName: this.form.arrivalName,
@@ -1732,6 +1859,9 @@ export default {
departureLatitude: this.form.arrivalLatitude,
departureRegionCode: this.form.arrivalRegionCode,
departureSiteCode: this.form.arrivalSiteCode,
departureProvinceName: this.form.arrivalProvinceName,
departureCityName: this.form.arrivalCityName,
departureDistrictName: this.form.arrivalDistrictName,
transportType: this.form.finalTransportType,
},
],
@@ -1887,57 +2017,59 @@ export default {
justify-content: flex-end;
margin-top: 12px;
}
.route-steps {
.route-flow {
position: relative;
.el-steps {
--el-text-color-placeholder: #a8abb2;
padding: 4px 0;
&::before {
position: absolute;
top: 20px;
bottom: 20px;
left: 16px;
width: 2px;
background: #dcdfe6;
content: '';
}
:deep(.el-step__main) {
padding-bottom: 16px;
}
.route-flow-row {
position: relative;
display: grid;
grid-template-columns: 32px minmax(0, 1fr);
column-gap: 16px;
align-items: start;
}
.route-flow-marker {
position: relative;
z-index: 1;
display: inline-flex;
width: 32px;
height: 32px;
align-items: center;
justify-content: center;
border-radius: 6px;
background: #409eff;
color: #fff;
font-size: 16px;
font-weight: 600;
line-height: 1;
&--middle {
background: #67c23a;
}
:deep(.el-step__title) {
font-size: 16px;
color: #303133;
}
:deep(.el-step__description) {
padding: 12px 0 0;
}
:deep(.el-step__line) {
background-color: #dcdfe6;
}
:deep(.el-step__icon) {
width: 32px;
height: 32px;
flex-shrink: 0;
border: 0;
background: transparent;
}
.route-step-icon {
display: inline-flex;
width: 32px;
height: 32px;
min-width: 32px;
min-height: 32px;
flex-shrink: 0;
align-items: center;
justify-content: center;
border-radius: 6px;
background: #409eff;
color: #fff;
font-size: 16px;
font-weight: 600;
line-height: 1;
&--middle {
background: #67c23a;
}
&--end {
background: #e6a23c;
}
&--end {
background: #e6a23c;
}
}
.route-flow-body {
min-width: 0;
}
.route-flow-segment {
min-height: 48px;
padding-left: 48px;
display: flex;
align-items: center;
}
.route-node-form {
display: grid;
grid-template-columns: repeat(2, minmax(420px, 1fr)) repeat(2, minmax(260px, 1fr));
grid-template-columns: 300px 360px repeat(3, 300px);
gap: 0 16px;
align-items: start;
:deep(.el-form-item) {
@@ -1963,9 +2095,24 @@ export default {
.route-address-name {
flex: 0 0 220px;
}
.route-address-detail,
.route-address-detail {
flex: 0 0 300px;
width: 300px;
}
.route-contact-actions :deep(.el-input) {
flex: 1;
flex: 0 0 200px;
width: 200px;
}
:deep(.route-contact-form-item .el-input),
:deep(.route-phone-form-item .el-input),
:deep(.route-date-form-item .el-date-editor) {
width: 200px;
}
:deep(.route-contact-actions) {
width: auto;
}
:deep(.el-form-item__label) {
white-space: nowrap;
}
.address-picker-button {
flex: 0 0 auto;
@@ -1985,6 +2132,7 @@ export default {
font-size: 16px;
.route-segment-label {
flex: 0 0 auto;
white-space: nowrap;
}
.route-title-path {
display: inline-block;
@@ -2010,6 +2158,10 @@ export default {
}
}
}
.required-asterisk {
margin-left: 4px;
color: #f56c6c;
}
.route-action-button {
flex: 0 0 auto;
}