1、新增结算模块
2、调整业务模块 3、调整客商模块
This commit is contained in:
@@ -63,7 +63,7 @@
|
||||
<div class="route-node-form">
|
||||
<el-form-item label="发货地址" required class="route-address-form-item"
|
||||
><div class="route-address-inputs"
|
||||
><el-input v-model="form.departureName" class="route-address-name" readonly placeholder="发货地址" @click="openAddressMap('departure')" /><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="List" @click="openCommonAddress('departure')" /></el-tooltip></div
|
||||
><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="List" @click="openCommonAddress('departure')" /></el-tooltip></div
|
||||
></el-form-item>
|
||||
<el-form-item label="联系人"
|
||||
><el-input v-model="form.departureContact" placeholder="请输入"
|
||||
@@ -74,9 +74,9 @@
|
||||
<el-form-item label="计划开始日期"
|
||||
><el-date-picker
|
||||
v-model="form.planStartTime"
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择"
|
||||
/></el-form-item>
|
||||
</div>
|
||||
@@ -84,12 +84,12 @@
|
||||
</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><span class="route-title-path">{{ getRouteTitle(index).replace(`段${index + 1}:`, '') }}</span><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 #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 class="route-node-form">
|
||||
<el-form-item label="途经地" required class="route-address-form-item"
|
||||
><div class="route-address-inputs"
|
||||
><el-input v-model="route.departureName" class="route-address-name" readonly :disabled="!route.transportType" placeholder="请先选择运输方式" @click="openAddressMap(`route-${index}`)" /><el-input v-model="route.departureAddress" class="route-address-detail" readonly :disabled="!route.transportType" placeholder="请先选择运输方式" @click="openAddressMap(`route-${index}`)" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="List" :disabled="!route.transportType" @click="openCommonAddress(`route-${index}`)" /></el-tooltip></div
|
||||
><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="List" :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>
|
||||
@@ -98,12 +98,12 @@
|
||||
</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><span class="route-title-path">{{ getFinalRouteTitle().replace(`段${form.routes.length + 1}:`, '') }}</span><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 #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 class="route-address-inputs"
|
||||
><el-input v-model="form.arrivalName" class="route-address-name" readonly :disabled="!form.finalTransportType" placeholder="请先选择运输方式" @click="openAddressMap('arrival')" /><el-input v-model="form.arrivalAddress" class="route-address-detail" readonly :disabled="!form.finalTransportType" placeholder="请先选择运输方式" @click="openAddressMap('arrival')" /><el-tooltip content="选择常用地址" placement="top"><el-button class="address-picker-button" type="primary" link :icon="List" :disabled="!form.finalTransportType" @click="openCommonAddress('arrival')" /></el-tooltip></div
|
||||
><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="List" :disabled="!form.finalTransportType" @click="openCommonAddress('arrival')" /></el-tooltip></div
|
||||
></el-form-item>
|
||||
<el-form-item label="联系人"
|
||||
><el-input v-model="form.arrivalContact" placeholder="请输入" /></el-form-item>
|
||||
@@ -112,9 +112,9 @@
|
||||
<el-form-item label="计划结束日期"
|
||||
><el-date-picker
|
||||
v-model="form.planEndTime"
|
||||
type="datetime"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="请选择"
|
||||
/></el-form-item>
|
||||
</div>
|
||||
@@ -299,6 +299,31 @@
|
||||
width="1100px"
|
||||
@opened="loadAddressList"
|
||||
>
|
||||
<div class="route-dialog__search common-address-dialog__search">
|
||||
<el-form :model="addressQuery" label-position="right" label-width="auto">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="地址名称">
|
||||
<el-input v-model="addressQuery.addressName" clearable placeholder="请输入" @keyup.enter="searchAddressList" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="详细地址">
|
||||
<el-input v-model="addressQuery.detailAddress" clearable placeholder="请输入" @keyup.enter="searchAddressList" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="行政区划">
|
||||
<el-input v-model="addressQuery.regionName" clearable placeholder="请输入" @keyup.enter="searchAddressList" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6" class="route-dialog__search-actions">
|
||||
<el-button type="primary" @click="searchAddressList">查询</el-button>
|
||||
<el-button @click="resetAddressList">重置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table v-loading="addressLoading" :data="addressRows" border height="360">
|
||||
<el-table-column type="index" label="序号" width="70" />
|
||||
<el-table-column prop="addressName" label="地址名称" min-width="150" />
|
||||
@@ -386,10 +411,22 @@
|
||||
<div class="address-pagination"><el-pagination v-model:current-page="stationPage.current" v-model:page-size="stationPage.size" :page-sizes="[10, 20, 50]" layout="total, prev, pager, next, sizes" :total="stationPage.total" @size-change="loadStationList" @current-change="loadStationList" /></div>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="commonCargoVisible" title="选择常用货物" append-to-body width="1100px" @opened="loadCommonCargoList">
|
||||
<el-table v-loading="commonCargoLoading" :data="commonCargoRows" border height="360">
|
||||
<div class="route-dialog__search common-cargo-dialog__search">
|
||||
<el-form :model="commonCargoQuery" label-position="right" label-width="86px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6"><el-form-item label="货物名称"><el-input v-model="commonCargoQuery.cargoName" clearable placeholder="请输入" /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="货物类型"><el-cascader v-model="commonCargoQuery.cargoTypePath" :options="cargoTypeOptions" :props="cargoTypeCascaderProps" clearable filterable placeholder="请选择" :loading="cargoTypeLoading" @visible-change="visible => visible && loadCargoTypeOptions()" @change="handleCommonCargoTypeChange" /></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="规格型号"><el-input v-model="commonCargoQuery.specificationModel" clearable placeholder="请输入" /></el-form-item></el-col>
|
||||
<el-col :span="6" class="route-dialog__search-actions"><el-button type="primary" @click="searchCommonCargoList">查询</el-button><el-button @click="resetCommonCargoList">重置</el-button></el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table v-loading="commonCargoLoading" :data="commonCargoRows" border height="360" @selection-change="handleCommonCargoSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||
<el-table-column type="index" label="序号" width="70" /><el-table-column prop="cargoName" label="货物名称" min-width="150" /><el-table-column prop="cargoCode" label="货物编号" min-width="130" /><el-table-column prop="firstCargoTypeName" label="一级货物类型" min-width="150" /><el-table-column prop="secondCargoTypeName" label="二级货物类型" min-width="150" /><el-table-column prop="packageType" label="包装" min-width="100" /><el-table-column prop="brand" label="品牌" min-width="120" /><el-table-column prop="specification" label="规格" min-width="120" /><el-table-column prop="model" label="型号" min-width="120" /><el-table-column label="操作" width="100" fixed="right"><template #default="{ row }"><el-link type="primary" @click="selectCommonCargo(row)">选择</el-link></template></el-table-column>
|
||||
</el-table>
|
||||
<div class="address-pagination"><el-pagination v-model:current-page="commonCargoPage.current" v-model:page-size="commonCargoPage.size" :page-sizes="[10, 20, 50]" layout="total, prev, pager, next, sizes" :total="commonCargoPage.total" @size-change="loadCommonCargoList" @current-change="loadCommonCargoList" /></div>
|
||||
<template #footer><el-button @click="commonCargoVisible = false">关闭</el-button><el-button type="primary" :disabled="!commonCargoSelected.length" @click="confirmCommonCargoSelection">确定</el-button></template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="cargoImportVisible" title="导入货物" append-to-body width="560px"><div class="cargo-import"><el-upload action="#" accept=".xls,.xlsx" :auto-upload="false" :show-file-list="false" :disabled="cargoImportLoading" :on-change="handleCargoImport"><el-button type="primary" :loading="cargoImportLoading">上传</el-button><template #tip><div class="el-upload__tip">支持 .xlsx、.xls 文件,单个文件不超过 50M</div></template></el-upload><el-button type="primary" plain @click="downloadCargoTemplate">点击下载模板</el-button></div><template #footer><el-button @click="cargoImportVisible = false">关闭</el-button></template></el-dialog>
|
||||
<el-dialog v-model="routeDialogVisible" title="选择线路" width="1280px" @opened="loadRouteList">
|
||||
@@ -485,6 +522,7 @@ import { getList as getAirportMasterList } from '@/api/base/airport-master';
|
||||
import { getList as getPortTerminalList } from '@/api/base/port-terminal';
|
||||
import { getList as getRailwayStationList } from '@/api/base/railway-station';
|
||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||
import { getLazyTree as getRegionLazyTree } from '@/api/base/region';
|
||||
import { getList as getCommonCargoList } from '@/api/business/common-cargo';
|
||||
import { exportBlob, importBlob } from '@/api/common';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
@@ -534,7 +572,12 @@ export default {
|
||||
addressLoading: false,
|
||||
addressTarget: '',
|
||||
addressRows: [],
|
||||
addressQuery: {},
|
||||
addressPage: { current: 1, size: 10, total: 0 },
|
||||
addressRegionPaths: { departure: [], arrival: [] },
|
||||
regionOptions: [],
|
||||
regionLoading: false,
|
||||
regionRequest: null,
|
||||
stationDialogVisible: false,
|
||||
stationLoading: false,
|
||||
stationQuery: {},
|
||||
@@ -552,6 +595,8 @@ export default {
|
||||
commonCargoVisible: false,
|
||||
commonCargoLoading: false,
|
||||
commonCargoRows: [],
|
||||
commonCargoQuery: {},
|
||||
commonCargoSelected: [],
|
||||
commonCargoPage: { current: 1, size: 10, total: 0 },
|
||||
cargoTypeLoading: false,
|
||||
cargoTypeOptions: [],
|
||||
@@ -625,11 +670,19 @@ export default {
|
||||
emitPath: true,
|
||||
};
|
||||
},
|
||||
regionCascaderProps() {
|
||||
return {
|
||||
value: 'id',
|
||||
label: 'title',
|
||||
children: 'children',
|
||||
emitPath: true,
|
||||
};
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
this.loading = true;
|
||||
try {
|
||||
await Promise.all([this.loadProjects(), this.loadCargoTypeOptions()]);
|
||||
await Promise.all([this.loadProjects(), this.loadCargoTypeOptions(), this.loadRegionOptions()]);
|
||||
if (this.id) {
|
||||
await this.loadDetail(this.id);
|
||||
}
|
||||
@@ -641,6 +694,118 @@ export default {
|
||||
responseData(response) {
|
||||
return response?.data?.data || response?.data || response || {};
|
||||
},
|
||||
async loadRegionOptions() {
|
||||
if (this.regionOptions.length) return this.regionOptions;
|
||||
if (this.regionRequest) return this.regionRequest;
|
||||
this.regionLoading = true;
|
||||
this.regionRequest = getRegionLazyTree()
|
||||
.then(response => {
|
||||
const data = response?.data?.data || response?.data || response || [];
|
||||
this.regionOptions = this.extractChinaRegionOptions(this.buildRegionTree(data));
|
||||
this.syncAddressRegionPaths();
|
||||
return this.regionOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
this.regionLoading = false;
|
||||
this.regionRequest = null;
|
||||
});
|
||||
return this.regionRequest;
|
||||
},
|
||||
buildRegionTree(regions = []) {
|
||||
const flat = [];
|
||||
const collect = list =>
|
||||
(list || []).forEach(item => {
|
||||
flat.push({ ...item, children: undefined });
|
||||
if (item.children?.length) collect(item.children);
|
||||
});
|
||||
collect(regions);
|
||||
const nodeMap = new Map();
|
||||
flat.forEach(item => {
|
||||
const id = item.id ?? item.code ?? item.value;
|
||||
if (id === undefined || id === null) return;
|
||||
nodeMap.set(String(id), {
|
||||
...item,
|
||||
id: String(id),
|
||||
parentId: String(item.parentId ?? item.parentCode ?? ''),
|
||||
children: [],
|
||||
});
|
||||
});
|
||||
const roots = [];
|
||||
nodeMap.forEach(node => {
|
||||
const parent = nodeMap.get(node.parentId);
|
||||
if (parent && parent !== node) parent.children.push(node);
|
||||
else roots.push(node);
|
||||
});
|
||||
const normalize = node => {
|
||||
const children = node.children.map(item => normalize(item));
|
||||
return { ...node, title: node.title || node.name || '', children: children.length ? children : undefined, leaf: !children.length };
|
||||
};
|
||||
return roots.map(item => normalize(item));
|
||||
},
|
||||
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;
|
||||
},
|
||||
findRegionPathByName(options = [], regionName = '', parents = []) {
|
||||
const target = String(regionName || '').replace(/\s+/g, '');
|
||||
if (!target) return [];
|
||||
for (const item of options) {
|
||||
const nextParents = [...parents, item];
|
||||
const name = nextParents.map(region => region.title || region.name || '').join('');
|
||||
if (name === target) return nextParents.map(region => region.id);
|
||||
const path = this.findRegionPathByName(item.children, target, nextParents);
|
||||
if (path.length) return path;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
findRegionPathByCode(options = [], regionCode = '', parents = []) {
|
||||
const target = String(regionCode || '').trim();
|
||||
if (!target) return [];
|
||||
for (const item of options) {
|
||||
const nextParents = [...parents, item];
|
||||
if (String(item.id) === target) return nextParents.map(region => region.id);
|
||||
const path = this.findRegionPathByCode(item.children, target, nextParents);
|
||||
if (path.length) return path;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
regionLabels(path = []) {
|
||||
let options = this.regionOptions;
|
||||
const labels = [];
|
||||
path.forEach(value => {
|
||||
const option = options.find(item => String(item.id) === String(value));
|
||||
if (!option) return;
|
||||
labels.push(option.title || option.name || '');
|
||||
options = option.children || [];
|
||||
});
|
||||
return labels;
|
||||
},
|
||||
handleAddressRegionChange(target, value) {
|
||||
const field = this.addressModel(target);
|
||||
const path = Array.isArray(value) ? value.map(item => String(item)) : [];
|
||||
this.addressRegionPaths = { ...this.addressRegionPaths, [target]: path };
|
||||
if (!field) return;
|
||||
const labels = this.regionLabels(path);
|
||||
field.model[field.name] = labels.join('');
|
||||
field.model[field.regionCode] = path.at(-1) || '';
|
||||
},
|
||||
syncAddressRegionPath(target) {
|
||||
const field = this.addressModel(target);
|
||||
if (!field) return;
|
||||
const codePath = this.findRegionPathByCode(this.regionOptions, field.model[field.regionCode]);
|
||||
const path = codePath.length
|
||||
? codePath
|
||||
: this.findRegionPathByName(this.regionOptions, field.model[field.name]);
|
||||
this.addressRegionPaths = { ...this.addressRegionPaths, [target]: path };
|
||||
},
|
||||
syncAddressRegionPaths() {
|
||||
if (!this.regionOptions.length) return;
|
||||
['departure', 'arrival', ...this.form.routes.map((route, index) => `route-${index}`)].forEach(target => {
|
||||
this.syncAddressRegionPath(target);
|
||||
});
|
||||
},
|
||||
async loadDetail(id) {
|
||||
const res = await api.getDetail(id);
|
||||
const detail = this.responseData(res);
|
||||
@@ -652,6 +817,8 @@ export default {
|
||||
finalSegment.departureName === detail.arrivalName;
|
||||
this.form = {
|
||||
...detail,
|
||||
planStartTime: this.normalizeDateValue(detail.planStartTime),
|
||||
planEndTime: this.normalizeDateValue(detail.planEndTime),
|
||||
routes: hasFinalSegment ? routes.slice(0, -1) : routes.length ? routes : [this.createRoute(0)],
|
||||
finalTransportType: hasFinalSegment
|
||||
? finalSegment.transportType
|
||||
@@ -660,6 +827,7 @@ export default {
|
||||
attachmentsJson: detail.attachmentsJson || '[]',
|
||||
transportOrganizationType: '多式联运',
|
||||
};
|
||||
this.syncAddressRegionPaths();
|
||||
this.restoreGoodsCargoTypePaths();
|
||||
this.attachmentRows = this.parseJsonArray(this.form.attachmentsJson);
|
||||
this.selectedAttachmentRows = [];
|
||||
@@ -675,17 +843,54 @@ export default {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
normalizeDateValue(value) {
|
||||
if (!value) return '';
|
||||
if (value instanceof Date) {
|
||||
const year = value.getFullYear();
|
||||
const month = String(value.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(value.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
return String(value).replace('T', ' ').slice(0, 10);
|
||||
},
|
||||
getRegionDisplay(target, fallback) {
|
||||
const labels = this.regionLabels(this.addressRegionPaths[target] || []);
|
||||
return labels.length ? labels.join('/') : fallback;
|
||||
},
|
||||
getRouteTitle(index) {
|
||||
const departureName =
|
||||
index === 0
|
||||
? this.form.departureName || '发货地址'
|
||||
: this.form.routes[index - 1].departureName || '途经地';
|
||||
return `段${index + 1}:${departureName} - ${this.form.routes[index].departureName || '途经地'}`;
|
||||
? this.getRegionDisplay('departure', this.form.departureName || '发货地址')
|
||||
: this.getRegionDisplay(
|
||||
`route-${index - 1}`,
|
||||
this.form.routes[index - 1].departureName || '途经地'
|
||||
);
|
||||
const arrivalName = this.getRegionDisplay(
|
||||
`route-${index}`,
|
||||
this.form.routes[index].departureName || '途经地'
|
||||
);
|
||||
return `段${index + 1}:${departureName} → ${arrivalName}`;
|
||||
},
|
||||
truncateRouteAddress(value) {
|
||||
const text = String(value || '');
|
||||
return text.length > 6 ? `${text.slice(0, 6)}...` : text;
|
||||
},
|
||||
formatRouteTitle(title, segmentNo) {
|
||||
const fullTitle = String(title || '').replace(`段${segmentNo}:`, '');
|
||||
const separator = ' → ';
|
||||
const separatorIndex = fullTitle.indexOf(separator);
|
||||
if (separatorIndex < 0) return this.truncateRouteAddress(fullTitle);
|
||||
const departure = fullTitle.slice(0, separatorIndex);
|
||||
const arrival = fullTitle.slice(separatorIndex + separator.length);
|
||||
return `${this.truncateRouteAddress(departure)}${separator}${this.truncateRouteAddress(arrival)}`;
|
||||
},
|
||||
getFinalRouteTitle() {
|
||||
return `段${this.form.routes.length + 1}:${this.lastRouteName} - ${
|
||||
this.form.arrivalName || '收货地址'
|
||||
}`;
|
||||
const departureName = this.getRegionDisplay(
|
||||
`route-${this.form.routes.length - 1}`,
|
||||
this.lastRouteName
|
||||
);
|
||||
const arrivalName = this.getRegionDisplay('arrival', this.form.arrivalName || '收货地址');
|
||||
return `段${this.form.routes.length + 1}:${departureName} → ${arrivalName}`;
|
||||
},
|
||||
responseRecords(response) {
|
||||
const data = response?.data?.data || response?.data || response;
|
||||
@@ -730,6 +935,7 @@ export default {
|
||||
phone: 'departurePhone',
|
||||
longitude: 'departureLongitude',
|
||||
latitude: 'departureLatitude',
|
||||
regionCode: 'departureRegionCode',
|
||||
siteCode: 'departureSiteCode',
|
||||
}
|
||||
: null;
|
||||
@@ -743,6 +949,7 @@ export default {
|
||||
phone: `${prefix}Phone`,
|
||||
longitude: `${prefix}Longitude`,
|
||||
latitude: `${prefix}Latitude`,
|
||||
regionCode: `${prefix}RegionCode`,
|
||||
siteCode: `${prefix}SiteCode`,
|
||||
};
|
||||
},
|
||||
@@ -759,6 +966,10 @@ export default {
|
||||
if (type.includes('航空') || type.includes('空运')) return 'air';
|
||||
return 'road';
|
||||
},
|
||||
isRoadTransportType(type) {
|
||||
const value = String(type || '').trim().toLowerCase();
|
||||
return value === 'road' || value.includes('公路');
|
||||
},
|
||||
openCommonAddress(target) {
|
||||
this.addressTarget = target;
|
||||
if (this.addressTransportMode(target) !== 'road') {
|
||||
@@ -767,6 +978,7 @@ export default {
|
||||
this.stationDialogVisible = true;
|
||||
return;
|
||||
}
|
||||
this.addressQuery = {};
|
||||
this.addressPage.current = 1;
|
||||
this.addressDialogVisible = true;
|
||||
},
|
||||
@@ -814,12 +1026,14 @@ export default {
|
||||
const target = this.addressModel(this.addressTarget);
|
||||
if (!target) return;
|
||||
const station = this.normalizeStation(row);
|
||||
target.model[target.name] = station.stationName;
|
||||
target.model[target.name] = station.regionName || station.stationName;
|
||||
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.siteCode] = station.stationCode;
|
||||
this.stationDialogVisible = false;
|
||||
this.syncAddressRegionPath(this.addressTarget);
|
||||
},
|
||||
async loadAddressList() {
|
||||
this.addressLoading = true;
|
||||
@@ -827,7 +1041,7 @@ export default {
|
||||
const response = await getCommonAddressList(
|
||||
this.addressPage.current,
|
||||
this.addressPage.size,
|
||||
{ allDept: 0 }
|
||||
{ ...this.addressQuery, allDept: 0 }
|
||||
);
|
||||
const data = response?.data?.data || response?.data || response || {};
|
||||
this.addressRows = data.records || [];
|
||||
@@ -836,18 +1050,33 @@ export default {
|
||||
this.addressLoading = false;
|
||||
}
|
||||
},
|
||||
searchAddressList() {
|
||||
this.addressPage.current = 1;
|
||||
this.loadAddressList();
|
||||
},
|
||||
resetAddressList() {
|
||||
this.addressQuery = {};
|
||||
this.addressPage.current = 1;
|
||||
this.loadAddressList();
|
||||
},
|
||||
selectAddress(row) {
|
||||
const target = this.addressModel(this.addressTarget);
|
||||
if (!target) return;
|
||||
target.model[target.name] = row.addressName || row.regionName || '';
|
||||
target.model[target.name] = row.regionName || row.addressName || '';
|
||||
target.model[target.address] = row.detailAddress || row.address || '';
|
||||
target.model[target.contact] = row.contactName || target.model[target.contact] || '';
|
||||
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 || '';
|
||||
this.addressDialogVisible = false;
|
||||
this.syncAddressRegionPath(this.addressTarget);
|
||||
},
|
||||
openAddressMap(target) {
|
||||
if ((target.startsWith('route-') || target === 'arrival') && !this.isRoadTransportType(this.addressTransportType(target))) {
|
||||
this.$message.info('非公路运输地址不能使用地图选择');
|
||||
return;
|
||||
}
|
||||
const field = this.addressModel(target);
|
||||
if (!field) return;
|
||||
this.mapTarget = target;
|
||||
@@ -855,6 +1084,7 @@ export default {
|
||||
this.mapSelected = {
|
||||
longitude: field.model[field.longitude] || '',
|
||||
latitude: field.model[field.latitude] || '',
|
||||
regionCode: field.model[field.regionCode] || '',
|
||||
detailAddress: this.mapKeyword,
|
||||
regionName: field.model[field.name] || '',
|
||||
};
|
||||
@@ -935,6 +1165,7 @@ export default {
|
||||
latitude,
|
||||
detailAddress: keyword || this.mapKeyword,
|
||||
regionName: '',
|
||||
regionCode: '',
|
||||
};
|
||||
this.renderAddressMarker([longitude, latitude]);
|
||||
this.mapStatus = '已选点,可确认回填';
|
||||
@@ -947,6 +1178,7 @@ export default {
|
||||
this.mapSelected.regionName = [component.province, component.city, component.district]
|
||||
.filter(Boolean)
|
||||
.join('');
|
||||
this.mapSelected.regionCode = component.adcode || '';
|
||||
this.mapKeyword = this.mapSelected.detailAddress;
|
||||
this.mapStatus = '已解析地址,可确认回填';
|
||||
}
|
||||
@@ -967,16 +1199,22 @@ export default {
|
||||
target.model[target.address] = this.mapSelected.detailAddress || this.mapKeyword;
|
||||
target.model[target.longitude] = this.mapSelected.longitude;
|
||||
target.model[target.latitude] = this.mapSelected.latitude;
|
||||
target.model[target.regionCode] = this.mapSelected.regionCode || '';
|
||||
this.mapDialogVisible = false;
|
||||
this.syncAddressRegionPath(this.mapTarget);
|
||||
},
|
||||
openCommonCargoDialog() {
|
||||
this.commonCargoQuery = {};
|
||||
this.commonCargoSelected = [];
|
||||
this.commonCargoPage.current = 1;
|
||||
this.commonCargoVisible = true;
|
||||
},
|
||||
async loadCommonCargoList() {
|
||||
this.commonCargoSelected = [];
|
||||
this.commonCargoLoading = true;
|
||||
try {
|
||||
const response = await getCommonCargoList(this.commonCargoPage.current, this.commonCargoPage.size, { allDept: 0 });
|
||||
const { cargoTypePath, ...query } = this.commonCargoQuery;
|
||||
const response = await getCommonCargoList(this.commonCargoPage.current, this.commonCargoPage.size, { ...query, allDept: 0 });
|
||||
const data = response?.data?.data || response?.data || response || {};
|
||||
this.commonCargoRows = data.records || [];
|
||||
this.commonCargoPage.total = data.total || 0;
|
||||
@@ -984,6 +1222,31 @@ export default {
|
||||
this.commonCargoLoading = false;
|
||||
}
|
||||
},
|
||||
searchCommonCargoList() {
|
||||
this.commonCargoPage.current = 1;
|
||||
this.loadCommonCargoList();
|
||||
},
|
||||
resetCommonCargoList() {
|
||||
this.commonCargoQuery = {};
|
||||
this.commonCargoPage.current = 1;
|
||||
this.loadCommonCargoList();
|
||||
},
|
||||
handleCommonCargoTypeChange(value) {
|
||||
const path = Array.isArray(value) ? value.map(item => String(item)) : [];
|
||||
const firstCargoType = this.getCargoTypeByPath(path.slice(0, 1));
|
||||
const secondCargoType = path.length > 1 ? this.getCargoTypeByPath(path) : null;
|
||||
this.commonCargoQuery = {
|
||||
...this.commonCargoQuery,
|
||||
cargoTypePath: path,
|
||||
firstCargoTypeName: firstCargoType?.label || '',
|
||||
firstCargoTypeCode: firstCargoType?.cargoCode || firstCargoType?.code || '',
|
||||
secondCargoTypeName: secondCargoType?.label || '',
|
||||
secondCargoTypeCode: secondCargoType?.cargoCode || secondCargoType?.code || '',
|
||||
};
|
||||
},
|
||||
handleCommonCargoSelectionChange(rows) {
|
||||
this.commonCargoSelected = rows || [];
|
||||
},
|
||||
async loadCargoTypeOptions() {
|
||||
if (this.cargoTypeOptions.length || this.cargoTypeLoading) return;
|
||||
this.cargoTypeLoading = true;
|
||||
@@ -1133,7 +1396,16 @@ export default {
|
||||
});
|
||||
},
|
||||
selectCommonCargo(row) {
|
||||
const cargo = this.normalizeCargoRow(row, this.resolveCargoTypePath(row));
|
||||
this.appendCommonCargoRows([row]);
|
||||
this.commonCargoVisible = false;
|
||||
},
|
||||
confirmCommonCargoSelection() {
|
||||
if (!this.commonCargoSelected.length) return;
|
||||
this.appendCommonCargoRows(this.commonCargoSelected);
|
||||
this.$message.success(`成功选择${this.commonCargoSelected.length}条常用货物`);
|
||||
this.commonCargoVisible = false;
|
||||
},
|
||||
appendCommonCargoRows(rows = []) {
|
||||
const firstGoods = this.form.goods[0];
|
||||
const isFirstGoodsEmpty =
|
||||
firstGoods &&
|
||||
@@ -1148,12 +1420,11 @@ export default {
|
||||
'remark',
|
||||
].some(key => firstGoods[key]);
|
||||
|
||||
if (isFirstGoodsEmpty) {
|
||||
this.form.goods.splice(0, 1, cargo);
|
||||
} else {
|
||||
this.form.goods.push(cargo);
|
||||
}
|
||||
this.commonCargoVisible = false;
|
||||
rows.forEach((row, index) => {
|
||||
const cargo = this.normalizeCargoRow(row, this.resolveCargoTypePath(row));
|
||||
if (isFirstGoodsEmpty && index === 0) this.form.goods.splice(0, 1, cargo);
|
||||
else this.form.goods.push(cargo);
|
||||
});
|
||||
},
|
||||
resolveCargoTypePath(row = {}) {
|
||||
return (
|
||||
@@ -1275,6 +1546,7 @@ export default {
|
||||
this.form.arrivalAddress = route.arrivalAddress || '';
|
||||
this.form.arrivalContact = route.arrivalContact || '';
|
||||
this.form.arrivalPhone = route.arrivalPhone || '';
|
||||
this.syncAddressRegionPaths();
|
||||
this.form.finalTransportType = '公路运输';
|
||||
if (!this.form.routes.length) this.addRoute();
|
||||
this.routeDialogVisible = false;
|
||||
@@ -1287,6 +1559,7 @@ export default {
|
||||
},
|
||||
removeRoute(index) {
|
||||
this.form.routes.splice(index, 1);
|
||||
this.syncAddressRegionPaths();
|
||||
},
|
||||
addGoods(index) {
|
||||
this.form.goods.splice(index + 1, 0, { quantityUnit: quantityUnitOptions[0] });
|
||||
@@ -1416,6 +1689,8 @@ export default {
|
||||
}
|
||||
const payload = {
|
||||
...this.form,
|
||||
planStartTime: this.normalizeDateValue(this.form.planStartTime),
|
||||
planEndTime: this.normalizeDateValue(this.form.planEndTime),
|
||||
attachmentsJson: JSON.stringify(this.attachmentRows),
|
||||
routes: [
|
||||
...this.form.routes.map((route, index) => ({ ...route, segmentNo: `段${index + 1}` })),
|
||||
@@ -1537,6 +1812,9 @@ export default {
|
||||
padding: 12px 12px 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.common-cargo-dialog__search {
|
||||
background: #fff;
|
||||
}
|
||||
.route-dialog__search-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -1638,9 +1916,14 @@ export default {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.route-title-path {
|
||||
display: inline-block;
|
||||
max-width: 24em;
|
||||
overflow: hidden;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.route-transport-control {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user