1、调整凭证
2、调整运单
This commit is contained in:
@@ -2047,7 +2047,7 @@
|
||||
v-model="attachmentRows"
|
||||
:readonly="dialogReadonly"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleAttachmentChange"
|
||||
@@ -4265,7 +4265,7 @@
|
||||
<vehicle-attachment-upload
|
||||
v-model="dispatchItemAttachmentRows"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleDispatchItemAttachmentChange"
|
||||
@@ -4965,7 +4965,10 @@
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
<div ref="transportAmap" class="business-crud-page__map"></div>
|
||||
<div class="map-picker-content">
|
||||
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
|
||||
<div ref="transportAmap" class="business-crud-page__map"></div>
|
||||
</div>
|
||||
<div class="business-crud-page__map-info">
|
||||
<span>{{ transportMapStatus }}</span>
|
||||
<span v-if="transportMapSelected.regionName"
|
||||
@@ -5500,7 +5503,7 @@ const defaultDispatchRow = () => ({
|
||||
remark: '',
|
||||
});
|
||||
|
||||
const taskCarrierTypes = ['承运商', '自运', '网货平台'];
|
||||
const taskCarrierTypes = ['承运商', '自运'];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -5735,6 +5738,7 @@ export default {
|
||||
transportMapKeyword: '',
|
||||
transportMapStatus: '可搜索地址或点击地图选点',
|
||||
transportMapSelected: {},
|
||||
transportMapSearchResults: [],
|
||||
transportAmap: null,
|
||||
transportAmapMarker: null,
|
||||
transportAmapGeocoder: null,
|
||||
@@ -6094,7 +6098,7 @@ export default {
|
||||
return this.taskCarrierTypes;
|
||||
},
|
||||
taskCarrierLabel() {
|
||||
return this.form.carrierType === '网货平台' ? '承运方' : '承运商';
|
||||
return '承运商';
|
||||
},
|
||||
taskCarrierValue: {
|
||||
get() {
|
||||
@@ -6258,7 +6262,7 @@ export default {
|
||||
return this.isDispatchCarrierRequired(this.dispatchItemForm.carrierType);
|
||||
},
|
||||
dispatchCarrierLabel() {
|
||||
return this.dispatchItemForm.carrierType === '网货平台' ? '承运方' : '承运商';
|
||||
return '承运商';
|
||||
},
|
||||
dispatchCarrierSelection: {
|
||||
get() {
|
||||
@@ -7155,7 +7159,7 @@ export default {
|
||||
this.$message.info('当前运单暂无详情数据');
|
||||
return;
|
||||
}
|
||||
this.$router.push({ path: '/business/waybill-manage', query: { detailId: row.id } });
|
||||
this.$router.push({ path: '/business/waybill-manage/detail', query: { id: row.id } });
|
||||
},
|
||||
transportPlanWaybillIndex(index) {
|
||||
return (
|
||||
@@ -8793,7 +8797,7 @@ export default {
|
||||
}
|
||||
},
|
||||
isTaskCarrierRequired(carrierType) {
|
||||
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
|
||||
return String(carrierType || '').trim() === '承运商';
|
||||
},
|
||||
resetWaybillCarrierContractState() {
|
||||
if (!this.isWaybillDetailLayout) return;
|
||||
@@ -9267,7 +9271,7 @@ export default {
|
||||
['vehicleNo', this.transportVehicleNoLabel],
|
||||
];
|
||||
if (this.isTaskCarrierRequired(row.carrierType)) {
|
||||
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
|
||||
requiredFields.push(['carrierName', '承运商']);
|
||||
}
|
||||
if (this.isWaybillDetailLayout && row.carrierType === '承运商') {
|
||||
requiredFields.push(['carrierContractId', '承运商合同']);
|
||||
@@ -10106,6 +10110,12 @@ export default {
|
||||
this.ensureTransportAmapGeocoder()
|
||||
.then(() => this.runAmapGeocode('location', keyword))
|
||||
.then(result => {
|
||||
this.transportMapSearchResults = (result.geocodes || []).map((item, index) => ({
|
||||
id: item.id || index,
|
||||
name: item.formattedAddress || keyword,
|
||||
address: item.formattedAddress || keyword,
|
||||
location: item.location,
|
||||
}));
|
||||
const point = this.resolveMapPoint(result);
|
||||
if (!point) {
|
||||
this.transportMapStatus = '未找到匹配地址';
|
||||
@@ -10126,6 +10136,11 @@ export default {
|
||||
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
|
||||
});
|
||||
},
|
||||
selectTransportMapSearchResult(item) {
|
||||
if (item && item.location) {
|
||||
this.pickTransportMapPoint(item.location, item.address || item.name || '');
|
||||
}
|
||||
},
|
||||
pickTransportMapPoint(lnglat, keyword) {
|
||||
const longitude = this.getPointLng(lnglat);
|
||||
const latitude = this.getPointLat(lnglat);
|
||||
@@ -12671,7 +12686,7 @@ export default {
|
||||
this.loadDispatchCarrierOptions(this.dispatchRow);
|
||||
},
|
||||
isDispatchCarrierRequired(carrierType) {
|
||||
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
|
||||
return String(carrierType || '').trim() === '承运商';
|
||||
},
|
||||
getProjectCarrierOptions(project = {}) {
|
||||
const rows = this.parseJsonArray(project.carrierJson);
|
||||
@@ -13058,7 +13073,7 @@ export default {
|
||||
],
|
||||
];
|
||||
if (this.isDispatchCarrierRequired(row.carrierType)) {
|
||||
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
|
||||
requiredFields.push(['carrierName', '承运商']);
|
||||
}
|
||||
if (row.carrierType === '承运商') {
|
||||
requiredFields.push(['carrierContractId', '承运商合同']);
|
||||
|
||||
@@ -144,7 +144,9 @@ export default {
|
||||
: [row.captainName, vehicle];
|
||||
return values.filter(Boolean).join(' / ') || '-';
|
||||
},
|
||||
openWaybill(row) { this.$router.push({ path: '/business/waybill-manage', query: { detailId: row.id } }); },
|
||||
openWaybill(row) {
|
||||
this.$router.push({ path: '/business/waybill-manage/detail', query: { id: row.id } });
|
||||
},
|
||||
openPlan(row) { this.$router.push({ path: '/business/transport-plan', query: { detailId: row.id } }); },
|
||||
waybillStatusName(value) { return ({ pending: '待执行', waiting: '待执行', running: '进行中', processing: '进行中', completed: '已完成', cancelled: '已取消' })[value] || value || '-'; },
|
||||
waybillStatusType(value) { return ({ pending: 'info', waiting: 'info', running: 'warning', processing: 'warning', completed: 'success', cancelled: 'danger' })[value] || 'info'; },
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
|
||||
<template v-if="route.documentType === '运单'">
|
||||
<div class="goods-heading"><h3>任务信息</h3></div>
|
||||
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-type-form"><el-form-item label="承运类型" required><el-radio-group v-model="route.carrierType" @change="value => handleCarrierTypeChange(route, value)"><el-radio-button label="承运商" /><el-radio-button label="自运" /><el-radio-button label="网货平台" /></el-radio-group></el-form-item></el-form>
|
||||
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-type-form"><el-form-item label="承运类型" required><el-radio-group v-model="route.carrierType" @change="value => handleCarrierTypeChange(route, value)"><el-radio-button label="承运商" /><el-radio-button label="自运" /></el-radio-group></el-form-item></el-form>
|
||||
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form">
|
||||
<el-row :gutter="16">
|
||||
<template v-if="isRoad(route)">
|
||||
@@ -862,7 +862,7 @@ export default {
|
||||
if (route.documentType === '运单') {
|
||||
if (this.isRoad(route)) {
|
||||
if (route.carrierType === '承运商' && (!route.carrierContractId || !route.carrierName || !route.vehicleNo)) return this.$message.warning('请选择承运商并填写车牌号');
|
||||
if (route.carrierType !== '承运商' && (!route.driverName || !route.driverPhone || !route.vehicleNo)) return this.$message.warning('请补全自运或网货平台的车辆与人员信息');
|
||||
if (route.carrierType !== '承运商' && (!route.driverName || !route.driverPhone || !route.vehicleNo)) return this.$message.warning('请补全自运的车辆与人员信息');
|
||||
} else if (!route.vehicleNo || (route.carrierType === '承运商' && (!route.carrierContractId || !route.carrierName))) {
|
||||
return this.$message.warning('请补全非公路运输的承运信息');
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
<div class="goods-heading">
|
||||
<h3>货物信息</h3>
|
||||
<div class="goods-toolbar">
|
||||
<el-link type="primary" @click="cargoImportVisible = true">导入货物</el-link>
|
||||
<!-- <el-link type="primary" @click="cargoImportVisible = true">导入货物</el-link> -->
|
||||
<el-link type="primary" @click="openCommonCargoDialog">常用货物</el-link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -283,7 +283,7 @@
|
||||
<vehicle-attachment-upload
|
||||
v-model="attachmentRows"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleAttachmentChange"
|
||||
@@ -401,7 +401,10 @@
|
||||
>搜索</el-button
|
||||
>
|
||||
</div>
|
||||
<div ref="addressMap" class="address-map" />
|
||||
<div class="map-picker-content">
|
||||
<map-search-results :results="mapSearchResults" @select="selectAddressMapSearchResult" />
|
||||
<div ref="addressMap" class="address-map" />
|
||||
</div>
|
||||
<div class="address-map-status">
|
||||
{{ mapStatus
|
||||
}}<span v-if="mapSelected.regionName"> 行政区划:{{ mapSelected.regionName }}</span>
|
||||
@@ -639,6 +642,7 @@ export default {
|
||||
mapKeyword: '',
|
||||
mapStatus: '可搜索地址或点击地图选点',
|
||||
mapSelected: {},
|
||||
mapSearchResults: [],
|
||||
addressMapInstance: null,
|
||||
addressMapMarker: null,
|
||||
addressMapGeocoder: null,
|
||||
@@ -1281,12 +1285,24 @@ export default {
|
||||
)
|
||||
)
|
||||
.then(result => {
|
||||
const point = result?.geocodes?.[0]?.location;
|
||||
const geocodes = result?.geocodes || [];
|
||||
this.mapSearchResults = geocodes.map((item, index) => ({
|
||||
id: item.id || index,
|
||||
name: item.formattedAddress || this.mapKeyword,
|
||||
address: item.formattedAddress || this.mapKeyword,
|
||||
location: item.location,
|
||||
}));
|
||||
const point = geocodes[0]?.location;
|
||||
if (!point) return this.$message.warning('地图搜索无匹配地址');
|
||||
this.pickAddressMap(point, this.mapKeyword);
|
||||
})
|
||||
.catch(() => this.$message.error('地图搜索失败'));
|
||||
},
|
||||
selectAddressMapSearchResult(item) {
|
||||
if (item && item.location) {
|
||||
this.pickAddressMap(item.location, item.address || item.name || '');
|
||||
}
|
||||
},
|
||||
ensureAddressGeocoder() {
|
||||
if (this.addressMapGeocoder) return Promise.resolve(this.addressMapGeocoder);
|
||||
return new Promise(resolve =>
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
>
|
||||
<span>货物信息</span>
|
||||
<div v-if="!dialogReadonly" class="shipping-template-page__section-actions">
|
||||
<el-link type="primary" @click="cargoImportBox = true">导入货物</el-link>
|
||||
<!-- <el-link type="primary" @click="cargoImportBox = true">导入货物</el-link> -->
|
||||
<el-link type="primary" @click="openCommonCargoDialog">常用货物</el-link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -233,7 +233,10 @@
|
||||
<div class="shipping-template-page__cargo-wrap">
|
||||
<el-table :data="transportCargoRows" border empty-text="暂无货物信息">
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column label="货物类型" min-width="200" align="center">
|
||||
<el-table-column min-width="200" align="center">
|
||||
<template #header>
|
||||
<span>货物类型<span class="goods-required-mark">*</span></span>
|
||||
</template>
|
||||
<template #default="{ row }">
|
||||
<el-cascader
|
||||
v-model="row.cargoTypePath"
|
||||
@@ -482,7 +485,7 @@
|
||||
v-model="attachmentRows"
|
||||
:readonly="dialogReadonly"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleAttachmentChange"
|
||||
@@ -925,7 +928,13 @@
|
||||
@keyup.enter="searchTransportMapKeyword"
|
||||
/><el-button type="primary" @click="searchTransportMapKeyword">搜索</el-button>
|
||||
</div>
|
||||
<div ref="transportMap" class="shipping-template-page__map" />
|
||||
<div class="map-picker-content">
|
||||
<map-search-results
|
||||
:results="transportMapSearchResults"
|
||||
@select="selectTransportMapSearchResult"
|
||||
/>
|
||||
<div ref="transportMap" class="shipping-template-page__map" />
|
||||
</div>
|
||||
<div class="shipping-template-page__map-info">{{ transportMapStatus }}</div>
|
||||
<template #footer
|
||||
><el-button @click="transportMapBox = false">取消</el-button
|
||||
@@ -946,12 +955,45 @@
|
||||
width="1200px"
|
||||
@opened="loadCommonCargoList"
|
||||
>
|
||||
<el-form :model="commonCargoQuery" inline
|
||||
><el-form-item label="货物名称"
|
||||
><el-input v-model="commonCargoQuery.cargoName" clearable /></el-form-item
|
||||
><el-button type="primary" @click="handleCommonCargoSearch">查询</el-button
|
||||
><el-button @click="handleCommonCargoReset">重置</el-button></el-form
|
||||
>
|
||||
<div class="shipping-template-page__dialog-search">
|
||||
<el-form :model="commonCargoQuery" label-position="right" label-width="100px">
|
||||
<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"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
filterable
|
||||
:loading="cargoTypeLoading"
|
||||
@visible-change="visible => visible && ensureCargoTypeOptions()"
|
||||
@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="shipping-template-page__dialog-search-actions">
|
||||
<el-button type="primary" @click="handleCommonCargoSearch">查询</el-button>
|
||||
<el-button @click="handleCommonCargoReset">重置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-table
|
||||
:data="commonCargoRows"
|
||||
border
|
||||
@@ -1001,35 +1043,6 @@
|
||||
>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="导入货物"
|
||||
v-model="cargoImportBox"
|
||||
append-to-body
|
||||
width="560px"
|
||||
@closed="cargoImportRows = []"
|
||||
>
|
||||
<el-upload
|
||||
action="#"
|
||||
accept=".xls,.xlsx"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
:disabled="cargoImportLoading"
|
||||
:on-change="handleCargoImportFileChange"
|
||||
><el-button type="primary" :loading="cargoImportLoading">上传</el-button></el-upload
|
||||
>
|
||||
<el-link type="primary" @click="handleCargoImportTemplate">下载模板</el-link>
|
||||
<template #footer
|
||||
><el-button @click="closeCargoImportDialog">关闭</el-button
|
||||
><el-button
|
||||
type="primary"
|
||||
:loading="cargoImportLoading"
|
||||
:disabled="!cargoImportRows.length"
|
||||
@click="confirmCargoImport"
|
||||
>确定</el-button
|
||||
></template
|
||||
>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="shippingTemplateProcessConfigBox"
|
||||
title="过程配置"
|
||||
@@ -1066,7 +1079,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { exportBlob, importBlob } from '@/api/common';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { getList as getCommonAddressList } from '@/api/base/common-address';
|
||||
import { getList as getAirportMasterList } from '@/api/base/airport-master';
|
||||
import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||
@@ -1320,6 +1333,7 @@ export default {
|
||||
transportMapLoading: false,
|
||||
transportMapStatus: '可搜索地址或点击地图选点',
|
||||
transportMapSelected: {},
|
||||
transportMapSearchResults: [],
|
||||
transportMapInstance: null,
|
||||
transportMapMarker: null,
|
||||
transportMapGeocoder: null,
|
||||
@@ -1329,9 +1343,6 @@ export default {
|
||||
commonCargoRows: [],
|
||||
commonCargoSelected: [],
|
||||
commonCargoPage: { pageSize: 10, currentPage: 1, total: 0 },
|
||||
cargoImportBox: false,
|
||||
cargoImportLoading: false,
|
||||
cargoImportRows: [],
|
||||
shippingTemplateProcessConfigBox: false,
|
||||
shippingTemplateProcessConfigLoading: false,
|
||||
shippingTemplateProcessConfigRows: [],
|
||||
@@ -1780,6 +1791,9 @@ export default {
|
||||
this.normalizeCargo(item)
|
||||
);
|
||||
if (!this.transportCargoRows.length) this.transportCargoRows = [defaultCargo()];
|
||||
this.restoreCargoTypePaths();
|
||||
// 编辑时先加载货物类型树,保证已有货物类型可以回显为级联路径。
|
||||
this.ensureCargoTypeOptions().catch(() => {});
|
||||
this.attachmentRows = this.parseJsonArray(this.form.attachmentsJson);
|
||||
this.shippingTemplateFreight = {
|
||||
...defaultFreight(),
|
||||
@@ -1846,7 +1860,11 @@ export default {
|
||||
return false;
|
||||
}
|
||||
for (const [index, row] of this.transportCargoRows.entries()) {
|
||||
if (!row.cargoType || !row.cargoName || !row.quantity || !row.quantityUnit) {
|
||||
if (!String(row.cargoType || '').trim() && !this.cargoPath(row.cargoTypePath).length) {
|
||||
this.$message.warning(`第${index + 1}行货物类型不能为空`);
|
||||
return false;
|
||||
}
|
||||
if (!String(row.cargoName || '').trim() || !row.quantity || !row.quantityUnit) {
|
||||
this.$message.warning(`第${index + 1}行货物信息不完整`);
|
||||
return false;
|
||||
}
|
||||
@@ -2339,6 +2357,7 @@ export default {
|
||||
regionName: this.form[`${prefix}Name`],
|
||||
addressCode: this.form[`${prefix}AddressCode`],
|
||||
});
|
||||
this.transportMapSearchResults = [];
|
||||
this.transportMapStatus = this.transportMapSelected.longitude
|
||||
? '已加载当前选点,可重新选点'
|
||||
: '可搜索地址或点击地图选点';
|
||||
@@ -2403,9 +2422,18 @@ export default {
|
||||
.then(() => this.ensureTransportMapGeocoder())
|
||||
.then(() => this.runTransportMapGeocode('location', keyword))
|
||||
.then(result => {
|
||||
this.transportMapSearchResults = (result.geocodes || []).map((item, index) => ({
|
||||
id: item.id || index,
|
||||
name: item.formattedAddress || keyword,
|
||||
address: item.formattedAddress || keyword,
|
||||
location: item.location,
|
||||
}));
|
||||
const point = this.resolveTransportMapPoint(result);
|
||||
if (!point) throw new Error('未找到匹配地址');
|
||||
return this.pickMapPoint(point, keyword);
|
||||
return this.$nextTick().then(() => {
|
||||
this.transportMapInstance?.resize();
|
||||
return this.pickMapPoint(point, keyword);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.transportMapStatus = '地图搜索失败';
|
||||
@@ -2415,6 +2443,11 @@ export default {
|
||||
this.transportMapLoading = false;
|
||||
});
|
||||
},
|
||||
selectTransportMapSearchResult(item) {
|
||||
if (item && item.location) {
|
||||
this.pickMapPoint(item.location, item.address || item.name || '');
|
||||
}
|
||||
},
|
||||
pickMapPoint(point, keyword = '') {
|
||||
const longitude = this.getTransportMapPointLng(point);
|
||||
const latitude = this.getTransportMapPointLat(point);
|
||||
@@ -2550,6 +2583,7 @@ export default {
|
||||
.then(res => {
|
||||
this.cargoTypeOptions = this.buildCargoTree(extractRecords(res));
|
||||
this.cargoTypeFlatOptions = this.flattenCargo(this.cargoTypeOptions);
|
||||
this.restoreCargoTypePaths();
|
||||
return this.cargoTypeOptions;
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -2603,13 +2637,81 @@ export default {
|
||||
cargoPath(path) {
|
||||
return Array.isArray(path) ? path.slice(0, 2).map(String) : [];
|
||||
},
|
||||
findCargoTypeOption(row = {}) {
|
||||
const path = this.cargoPath(row.cargoTypePath);
|
||||
if (path.length) {
|
||||
const pathKey = path.join('/');
|
||||
const pathMatch = this.cargoTypeFlatOptions.find(item => item.path?.join('/') === pathKey);
|
||||
if (pathMatch) return pathMatch;
|
||||
}
|
||||
const codeCandidates = [row.secondCargoTypeCode, row.cargoTypeCode, row.firstCargoTypeCode]
|
||||
.map(value => String(value || '').trim())
|
||||
.filter(Boolean);
|
||||
const nameCandidates = [
|
||||
row.secondCargoTypeName,
|
||||
row.cargoType,
|
||||
row.goodsType,
|
||||
row.firstCargoTypeName,
|
||||
]
|
||||
.map(value => String(value || '').trim())
|
||||
.filter(Boolean);
|
||||
return (
|
||||
this.cargoTypeFlatOptions.find(item =>
|
||||
codeCandidates.some(
|
||||
code => code === String(item.cargoCode || item.code || item.id || '').trim()
|
||||
)
|
||||
) ||
|
||||
this.cargoTypeFlatOptions.find(item =>
|
||||
nameCandidates.some(name => name === String(item.cargoName || '').trim())
|
||||
)
|
||||
);
|
||||
},
|
||||
resolveCargoTypePath(row = {}) {
|
||||
const matched = this.findCargoTypeOption(row);
|
||||
return matched?.path ? this.cargoPath(matched.path) : [];
|
||||
},
|
||||
restoreCargoTypePaths() {
|
||||
if (!this.transportCargoRows.length || !this.cargoTypeFlatOptions.length) return;
|
||||
this.transportCargoRows.forEach(row => {
|
||||
const path = this.resolveCargoTypePath(row);
|
||||
if (!path.length) return;
|
||||
const item = this.cargoTypeFlatOptions.find(
|
||||
option => option.path?.join('/') === path.join('/')
|
||||
);
|
||||
row.cargoTypePath = path;
|
||||
row.cargoType =
|
||||
item?.cargoName ||
|
||||
row.cargoType ||
|
||||
row.secondCargoTypeName ||
|
||||
row.firstCargoTypeName ||
|
||||
'';
|
||||
row.cargoTypeCode =
|
||||
item?.cargoCode ||
|
||||
row.cargoTypeCode ||
|
||||
row.secondCargoTypeCode ||
|
||||
row.firstCargoTypeCode ||
|
||||
'';
|
||||
});
|
||||
},
|
||||
normalizeCargo(row = {}) {
|
||||
const cargoTypePath = this.resolveCargoTypePath(row);
|
||||
const cargoType = this.findCargoTypeOption({ ...row, cargoTypePath });
|
||||
return {
|
||||
...defaultCargo(),
|
||||
...row,
|
||||
cargoTypePath: this.cargoPath(row.cargoTypePath),
|
||||
cargoType: row.cargoType || row.secondCargoTypeName || row.firstCargoTypeName || '',
|
||||
cargoTypeCode: row.cargoTypeCode || row.secondCargoTypeCode || row.firstCargoTypeCode || '',
|
||||
cargoTypePath,
|
||||
cargoType:
|
||||
cargoType?.cargoName ||
|
||||
row.cargoType ||
|
||||
row.secondCargoTypeName ||
|
||||
row.firstCargoTypeName ||
|
||||
'',
|
||||
cargoTypeCode:
|
||||
cargoType?.cargoCode ||
|
||||
row.cargoTypeCode ||
|
||||
row.secondCargoTypeCode ||
|
||||
row.firstCargoTypeCode ||
|
||||
'',
|
||||
cargoName: row.cargoName || row.goodsName || '',
|
||||
};
|
||||
},
|
||||
@@ -2623,7 +2725,7 @@ export default {
|
||||
.filter(Boolean);
|
||||
row.cargoTypePath = path;
|
||||
row.cargoType = labels.at(-1) || '';
|
||||
row.cargoTypeCode = item?.cargoCode || '';
|
||||
row.cargoTypeCode = item?.cargoCode || item?.code || item?.id || '';
|
||||
row.cargoName = '';
|
||||
},
|
||||
fetchCargoSuggestions(query, callback, row) {
|
||||
@@ -2637,8 +2739,28 @@ export default {
|
||||
)
|
||||
.catch(() => callback([]));
|
||||
},
|
||||
selectCargoSuggestion(row, item) {
|
||||
Object.assign(row, this.normalizeCargo(item));
|
||||
async selectCargoSuggestion(row, item = {}) {
|
||||
const cargoTypeOption = this.findCargoTypeOption(item);
|
||||
const selectedCargo = {
|
||||
...row,
|
||||
...item,
|
||||
cargoName: item.cargoName || item.name || item.label || item.value || row.cargoName || '',
|
||||
};
|
||||
try {
|
||||
await this.ensureCargoTypeOptions();
|
||||
} catch (error) {
|
||||
// 货物类型树加载失败时仍保留货物名称,提交时会给出货物类型必填提示。
|
||||
}
|
||||
// 货物名称返回了类型信息时,以货物自身的类型覆盖当前行原有类型。
|
||||
// 这样从一个类型切换到另一个类型时,级联选择器不会继续显示旧值。
|
||||
const matchedCargoType = this.findCargoTypeOption(item) || cargoTypeOption;
|
||||
if (matchedCargoType?.path?.length) {
|
||||
selectedCargo.cargoTypePath = this.cargoPath(matchedCargoType.path);
|
||||
selectedCargo.cargoType = matchedCargoType.cargoName || selectedCargo.cargoType;
|
||||
selectedCargo.cargoTypeCode =
|
||||
matchedCargoType.cargoCode || matchedCargoType.code || matchedCargoType.id || '';
|
||||
}
|
||||
Object.assign(row, this.normalizeCargo(selectedCargo));
|
||||
row.value = undefined;
|
||||
this.syncFreightItems();
|
||||
},
|
||||
@@ -2786,13 +2908,16 @@ export default {
|
||||
},
|
||||
openCommonCargoDialog() {
|
||||
this.commonCargoSelected = [];
|
||||
this.commonCargoQuery = {};
|
||||
this.commonCargoPage.currentPage = 1;
|
||||
this.commonCargoBox = true;
|
||||
this.ensureCargoTypeOptions().catch(() => {});
|
||||
},
|
||||
loadCommonCargoList() {
|
||||
this.commonCargoLoading = true;
|
||||
const { cargoTypePath, ...query } = this.commonCargoQuery || {};
|
||||
getCommonCargoList(this.commonCargoPage.currentPage, this.commonCargoPage.pageSize, {
|
||||
...this.normalizeSearch(this.commonCargoQuery),
|
||||
...this.normalizeSearch(query),
|
||||
allDept: 0,
|
||||
})
|
||||
.then(res => {
|
||||
@@ -2812,6 +2937,29 @@ export default {
|
||||
this.commonCargoQuery = {};
|
||||
this.handleCommonCargoSearch();
|
||||
},
|
||||
handleCommonCargoTypeChange(value) {
|
||||
const path = this.cargoPath(value);
|
||||
const firstCargoType = this.cargoTypeFlatOptions.find(
|
||||
item => item.path?.join('/') === path.slice(0, 1).join('/')
|
||||
);
|
||||
const secondCargoType = this.cargoTypeFlatOptions.find(
|
||||
item => item.path?.join('/') === path.join('/')
|
||||
);
|
||||
this.commonCargoQuery = {
|
||||
...this.commonCargoQuery,
|
||||
cargoTypePath: path,
|
||||
firstCargoTypeName: firstCargoType?.cargoName || '',
|
||||
firstCargoTypeCode: firstCargoType?.cargoCode || '',
|
||||
secondCargoTypeName: path.length > 1 ? secondCargoType?.cargoName || '' : '',
|
||||
secondCargoTypeCode: path.length > 1 ? secondCargoType?.cargoCode || '' : '',
|
||||
};
|
||||
if (!path.length) {
|
||||
delete this.commonCargoQuery.firstCargoTypeName;
|
||||
delete this.commonCargoQuery.firstCargoTypeCode;
|
||||
delete this.commonCargoQuery.secondCargoTypeName;
|
||||
delete this.commonCargoQuery.secondCargoTypeCode;
|
||||
}
|
||||
},
|
||||
handleCommonCargoCurrentChange(page) {
|
||||
this.commonCargoPage.currentPage = page;
|
||||
this.loadCommonCargoList();
|
||||
@@ -2823,54 +2971,16 @@ export default {
|
||||
handleCommonCargoSelectionChange(rows) {
|
||||
this.commonCargoSelected = rows || [];
|
||||
},
|
||||
selectCommonCargoRow(row) {
|
||||
async selectCommonCargoRow(row) {
|
||||
await this.ensureCargoTypeOptions().catch(() => {});
|
||||
this.addCargoRow(-1, row);
|
||||
this.commonCargoBox = false;
|
||||
},
|
||||
confirmCommonCargoSelection() {
|
||||
async confirmCommonCargoSelection() {
|
||||
await this.ensureCargoTypeOptions().catch(() => {});
|
||||
this.commonCargoSelected.forEach(row => this.addCargoRow(-1, row));
|
||||
this.commonCargoBox = false;
|
||||
},
|
||||
handleCargoImportFileChange(file) {
|
||||
const raw = file.raw;
|
||||
if (!raw || !/\.(xls|xlsx)$/i.test(raw.name || ''))
|
||||
return this.$message.warning('请上传 Excel 文件');
|
||||
this.cargoImportLoading = true;
|
||||
importBlob('/blade-transport/shipping-template/import-goods', raw)
|
||||
.then(async res => {
|
||||
const type = res.headers?.['content-type'] || res.data?.type || '';
|
||||
if (type.includes('excel')) {
|
||||
downloadXls(
|
||||
res.data,
|
||||
`发货模板货物导入失败明细${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
|
||||
);
|
||||
this.$message.warning('部分数据导入失败,已下载失败明细');
|
||||
return;
|
||||
}
|
||||
const result = JSON.parse(await res.data.text());
|
||||
if (result.code !== 200) throw new Error(result.msg);
|
||||
this.cargoImportRows = result.data || [];
|
||||
})
|
||||
.catch(() => this.$message.error('货物导入失败,请检查文件格式'))
|
||||
.finally(() => {
|
||||
this.cargoImportLoading = false;
|
||||
});
|
||||
},
|
||||
confirmCargoImport() {
|
||||
this.cargoImportRows.forEach(row => this.addCargoRow(-1, row));
|
||||
this.closeCargoImportDialog();
|
||||
},
|
||||
closeCargoImportDialog() {
|
||||
if (!this.cargoImportLoading) {
|
||||
this.cargoImportBox = false;
|
||||
this.cargoImportRows = [];
|
||||
}
|
||||
},
|
||||
handleCargoImportTemplate() {
|
||||
exportBlob('/blade-transport/shipping-template/export-goods-template').then(res =>
|
||||
downloadXls(res.data, '发货模板货物导入模板.xlsx')
|
||||
);
|
||||
},
|
||||
handleAttachmentChange(list) {
|
||||
const name = this.userInfo?.realName || this.userInfo?.userName || '';
|
||||
const time = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
@@ -3044,6 +3154,10 @@ export default {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.goods-required-mark {
|
||||
margin-left: 2px;
|
||||
color: #f56c6c;
|
||||
}
|
||||
.shipping-template-page__cargo-total {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
@@ -3388,10 +3502,27 @@ export default {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.shipping-template-page__dialog-search {
|
||||
margin-bottom: 8px;
|
||||
padding: 12px 12px 4px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
.shipping-template-page__dialog-search-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.shipping-template-page__map {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: 480px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
:deep(.shipping-template-page__map > .amap-container) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
.shipping-template-page__map-info {
|
||||
margin-top: 8px;
|
||||
color: #606266;
|
||||
|
||||
@@ -301,7 +301,7 @@
|
||||
>
|
||||
<span>货物信息</span>
|
||||
<div v-if="!dialogReadonly" class="transport-plan-page__section-actions">
|
||||
<el-link type="primary" @click="cargoImportBox = true">导入货物</el-link>
|
||||
<!-- <el-link type="primary" @click="cargoImportBox = true">导入货物</el-link> -->
|
||||
<el-link type="primary" @click="openCommonCargoDialog">常用货物</el-link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -526,7 +526,7 @@
|
||||
v-model="attachmentRows"
|
||||
:readonly="dialogReadonly"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleAttachmentChange"
|
||||
@@ -681,10 +681,8 @@
|
||||
<div class="transport-plan-page__transport-plan-detail-route-point">
|
||||
<b class="is-start">起</b>
|
||||
<div>
|
||||
<strong>{{
|
||||
detailRow.departureName || detailRow.departureAddress || '-'
|
||||
}}</strong>
|
||||
<p>{{ formatTransportPlanProvinceCityDistrict(detailRow.departureAddress) }}</p>
|
||||
<strong>{{ formatTransportPlanDetailRouteName('departure') }}</strong>
|
||||
<p>{{ detailRow.departureAddress || '-' }}</p>
|
||||
<p>
|
||||
{{
|
||||
[detailRow.departureContact, detailRow.departurePhone]
|
||||
@@ -698,8 +696,8 @@
|
||||
<div class="transport-plan-page__transport-plan-detail-route-point">
|
||||
<b class="is-end">终</b>
|
||||
<div>
|
||||
<strong>{{ detailRow.arrivalName || detailRow.arrivalAddress || '-' }}</strong>
|
||||
<p>{{ formatTransportPlanProvinceCityDistrict(detailRow.arrivalAddress) }}</p>
|
||||
<strong>{{ formatTransportPlanDetailRouteName('arrival') }}</strong>
|
||||
<p>{{ detailRow.arrivalAddress || '-' }}</p>
|
||||
<p>
|
||||
{{
|
||||
[detailRow.arrivalContact, detailRow.arrivalPhone]
|
||||
@@ -1558,7 +1556,7 @@
|
||||
>
|
||||
<span>货物信息</span>
|
||||
<div class="transport-plan-page__section-actions">
|
||||
<el-link type="primary" @click="cargoImportBox = true">导入货物</el-link>
|
||||
<!-- <el-link type="primary" @click="cargoImportBox = true">导入货物</el-link> -->
|
||||
<el-link type="primary" @click="openCommonCargoDialog">常用货物</el-link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2201,7 +2199,7 @@
|
||||
<vehicle-attachment-upload
|
||||
v-model="dispatchItemAttachmentRows"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleDispatchItemAttachmentChange"
|
||||
@@ -2518,7 +2516,10 @@
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
<div ref="transportAmap" class="transport-plan-page__map"></div>
|
||||
<div class="map-picker-content">
|
||||
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
|
||||
<div ref="transportAmap" class="transport-plan-page__map"></div>
|
||||
</div>
|
||||
<div class="transport-plan-page__map-info">
|
||||
<span>{{ transportMapStatus }}</span>
|
||||
<span v-if="transportMapSelected.regionName"
|
||||
@@ -2689,7 +2690,10 @@ import { getList as getCargoTypeList } from '@/api/base/cargo-type';
|
||||
import { getList as getPortTerminalList } from '@/api/base/port-terminal';
|
||||
import { getList as getRailwayStationList } from '@/api/base/railway-station';
|
||||
import { getList as getCommonCargoList } from '@/api/business/common-cargo';
|
||||
import { getList as getContractList } from '@/api/business/contract-manage';
|
||||
import {
|
||||
getDetail as getContractDetail,
|
||||
getList as getContractList,
|
||||
} from '@/api/business/contract-manage';
|
||||
import {
|
||||
getDetail as getProjectDetail,
|
||||
getList as getProjectList,
|
||||
@@ -2945,7 +2949,7 @@ const defaultDispatchRow = () => ({
|
||||
remark: '',
|
||||
});
|
||||
|
||||
const taskCarrierTypes = ['承运商', '自运', '网货平台'];
|
||||
const taskCarrierTypes = ['承运商', '自运'];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -3121,6 +3125,7 @@ export default {
|
||||
transportMapKeyword: '',
|
||||
transportMapStatus: '可搜索地址或点击地图选点',
|
||||
transportMapSelected: {},
|
||||
transportMapSearchResults: [],
|
||||
transportAmap: null,
|
||||
transportAmapMarker: null,
|
||||
transportAmapGeocoder: null,
|
||||
@@ -3446,7 +3451,7 @@ export default {
|
||||
return this.isDispatchCarrierRequired(this.dispatchItemForm.carrierType);
|
||||
},
|
||||
dispatchCarrierLabel() {
|
||||
return this.dispatchItemForm.carrierType === '网货平台' ? '承运方' : '承运商';
|
||||
return '承运商';
|
||||
},
|
||||
dispatchCarrierSelection: {
|
||||
get() {
|
||||
@@ -3790,6 +3795,34 @@ export default {
|
||||
const cityMatch = text.match(/市/);
|
||||
return cityMatch ? text.slice(0, cityMatch.index + 1) : text;
|
||||
},
|
||||
formatTransportPlanRoadCityDistrict(value) {
|
||||
const text = String(value || '').trim();
|
||||
if (!text) return '-';
|
||||
const cityMatch = text.match(/市/);
|
||||
if (!cityMatch) return this.formatTransportPlanProvinceCityDistrict(text);
|
||||
|
||||
const cityEnd = cityMatch.index + 1;
|
||||
const cityWithProvince = text.slice(0, cityEnd);
|
||||
const city =
|
||||
cityWithProvince.match(/(?:省|自治区|特别行政区)([^省]+市)$/)?.[1] || cityWithProvince;
|
||||
const districtText = text.slice(cityEnd);
|
||||
const districtMatch = districtText.match(
|
||||
/^(?:.*(?:自治县|自治旗|林区|矿区|新区|开发区|区|县|旗))/
|
||||
);
|
||||
if (!districtMatch) return city;
|
||||
|
||||
const district = districtMatch[0];
|
||||
return district ? `${city} ${district}` : city;
|
||||
},
|
||||
formatTransportPlanDetailRouteName(type) {
|
||||
const nameProp = type === 'departure' ? 'departureName' : 'arrivalName';
|
||||
const addressProp = type === 'departure' ? 'departureAddress' : 'arrivalAddress';
|
||||
const name = this.detailRow[nameProp] || this.detailRow[addressProp];
|
||||
const transportMode = this.resolveTransportMode(this.detailRow.transportType);
|
||||
return transportMode === 'road'
|
||||
? this.formatTransportPlanRoadCityDistrict(name)
|
||||
: name || '-';
|
||||
},
|
||||
hasPermission(code) {
|
||||
return this.isAdmin || this.validData(this.permission && this.permission[code], false);
|
||||
},
|
||||
@@ -3988,7 +4021,7 @@ export default {
|
||||
this.$message.info('当前运单暂无详情数据');
|
||||
return;
|
||||
}
|
||||
this.$router.push({ path: '/business/waybill-manage', query: { detailId: row.id } });
|
||||
this.$router.push({ path: '/business/waybill-manage/detail', query: { id: row.id } });
|
||||
},
|
||||
transportPlanWaybillIndex(index) {
|
||||
return (
|
||||
@@ -4054,10 +4087,28 @@ export default {
|
||||
request
|
||||
.then(res => {
|
||||
const detail = res?.data?.data || res?.data || row;
|
||||
this.detailRow = detail;
|
||||
this.ensureTransportPlanDetailTransportTypeName(detail);
|
||||
this.applyFormDetail(detail);
|
||||
return null;
|
||||
if (!detail.contractId) {
|
||||
this.detailRow = detail;
|
||||
this.ensureTransportPlanDetailTransportTypeName(detail);
|
||||
this.applyFormDetail(detail);
|
||||
return null;
|
||||
}
|
||||
return getContractDetail(detail.contractId)
|
||||
.then(contractRes => {
|
||||
const contract = contractRes?.data?.data || contractRes?.data || {};
|
||||
this.detailRow = {
|
||||
...detail,
|
||||
customerName:
|
||||
contract.partyA || contract.customerName || detail.customerName || '',
|
||||
};
|
||||
this.ensureTransportPlanDetailTransportTypeName(this.detailRow);
|
||||
this.applyFormDetail(this.detailRow);
|
||||
})
|
||||
.catch(() => {
|
||||
this.detailRow = detail;
|
||||
this.ensureTransportPlanDetailTransportTypeName(detail);
|
||||
this.applyFormDetail(detail);
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.detailLoading = false;
|
||||
@@ -4610,7 +4661,11 @@ export default {
|
||||
this.form.settlementCurrency =
|
||||
contract.settlementCurrency || contract.settlementCurrencyCode || contract.currency || '';
|
||||
this.form.customerName =
|
||||
contract.customerName || contract.customerNames || this.form.customerName || '';
|
||||
contract.partyA ||
|
||||
contract.customerName ||
|
||||
contract.customerNames ||
|
||||
this.form.customerName ||
|
||||
'';
|
||||
this.$refs.crud?.validateField?.('contractName');
|
||||
},
|
||||
loadDispatchContractCurrency(plan = {}) {
|
||||
@@ -5505,6 +5560,12 @@ export default {
|
||||
this.ensureTransportAmapGeocoder()
|
||||
.then(() => this.runAmapGeocode('location', keyword))
|
||||
.then(result => {
|
||||
this.transportMapSearchResults = (result.geocodes || []).map((item, index) => ({
|
||||
id: item.id || index,
|
||||
name: item.formattedAddress || keyword,
|
||||
address: item.formattedAddress || keyword,
|
||||
location: item.location,
|
||||
}));
|
||||
const point = this.resolveMapPoint(result);
|
||||
if (!point) {
|
||||
this.transportMapStatus = '未找到匹配地址';
|
||||
@@ -5525,6 +5586,11 @@ export default {
|
||||
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
|
||||
});
|
||||
},
|
||||
selectTransportMapSearchResult(item) {
|
||||
if (item && item.location) {
|
||||
this.pickTransportMapPoint(item.location, item.address || item.name || '');
|
||||
}
|
||||
},
|
||||
pickTransportMapPoint(lnglat, keyword) {
|
||||
const longitude = this.getPointLng(lnglat);
|
||||
const latitude = this.getPointLat(lnglat);
|
||||
@@ -6246,8 +6312,7 @@ export default {
|
||||
},
|
||||
handleImport() {
|
||||
if (this.config.enableTransportPlanImport) {
|
||||
this.transportPlanImportBox = true;
|
||||
this.loadProjectOptions();
|
||||
this.$router.push('/business/transport-plan/import');
|
||||
return;
|
||||
}
|
||||
this.$message.warning('运输计划导入接口未配置');
|
||||
@@ -6637,33 +6702,13 @@ export default {
|
||||
this.$message.warning('运输计划数据异常,无法调度');
|
||||
return;
|
||||
}
|
||||
this.dispatchRow = { ...row };
|
||||
this.dispatchCarrierOptions = [];
|
||||
this.dispatchCarrierContractOptions = [];
|
||||
this.dispatchCarrierRequestId += 1;
|
||||
this.dispatchBox = true;
|
||||
this.dispatchLoading = true;
|
||||
this.dispatchRows = [];
|
||||
this.loadDispatchTransportTypeOptions();
|
||||
const request =
|
||||
this.api && typeof this.api.getDetail === 'function'
|
||||
? this.api.getDetail(row.id)
|
||||
: Promise.resolve({ data: { data: row } });
|
||||
request
|
||||
.then(res => {
|
||||
const detail = {
|
||||
...row,
|
||||
...(res?.data?.data || {}),
|
||||
};
|
||||
this.dispatchRow = detail;
|
||||
this.dispatchRows = this.buildDispatchRows(detail);
|
||||
return this.loadDispatchContractCurrency(detail).then(() => {
|
||||
this.loadDispatchCarrierOptions(detail);
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.dispatchLoading = false;
|
||||
});
|
||||
// 跳转到独立的调度页面
|
||||
this.$router.push({
|
||||
path: '/business/transport-plan-dispatch',
|
||||
query: {
|
||||
planId: row.id,
|
||||
},
|
||||
});
|
||||
},
|
||||
loadDispatchTransportTypeOptions() {
|
||||
if (this.dispatchTransportTypeOptions.length || this.dispatchTransportTypeLoading) {
|
||||
@@ -7308,7 +7353,7 @@ export default {
|
||||
this.loadDispatchCarrierOptions(this.dispatchRow);
|
||||
},
|
||||
isDispatchCarrierRequired(carrierType) {
|
||||
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
|
||||
return String(carrierType || '').trim() === '承运商';
|
||||
},
|
||||
getProjectCarrierOptions(project = {}) {
|
||||
const rows = this.parseJsonArray(project.carrierJson);
|
||||
@@ -7748,7 +7793,7 @@ export default {
|
||||
],
|
||||
];
|
||||
if (this.isDispatchCarrierRequired(row.carrierType)) {
|
||||
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
|
||||
requiredFields.push(['carrierName', '承运商']);
|
||||
}
|
||||
if (row.carrierType === '承运商') {
|
||||
requiredFields.push(['carrierContractId', '承运商合同']);
|
||||
|
||||
@@ -119,18 +119,22 @@
|
||||
label="运单批次号"
|
||||
min-width="140"
|
||||
/><el-table-column prop="originalNo" label="原始单号" min-width="130" /><el-table-column
|
||||
prop="loadingIdentifier"
|
||||
label="配载标识号"
|
||||
min-width="130"
|
||||
/><el-table-column
|
||||
prop="vehicleNo"
|
||||
label="车牌号/航班号/船号/班列号"
|
||||
min-width="190"
|
||||
/><el-table-column prop="driverName" label="司机/船长" min-width="120" /><el-table-column
|
||||
prop="transportType"
|
||||
label="运输方式"
|
||||
width="110"
|
||||
/><el-table-column prop="cargoName" label="货物名称" width="130" /><el-table-column
|
||||
prop="cargoType"
|
||||
label="货物类型"
|
||||
width="120"
|
||||
/><el-table-column prop="quantity" label="重量" width="90" /><el-table-column
|
||||
/><el-table-column prop="transportType" label="运输方式" width="110" /><el-table-column
|
||||
prop="driverName"
|
||||
label="司机/船长姓名"
|
||||
min-width="120"
|
||||
/><el-table-column
|
||||
prop="driverPhone"
|
||||
label="司机/船长手机号"
|
||||
width="140"
|
||||
/><el-table-column
|
||||
prop="departureAddress"
|
||||
label="发货地址"
|
||||
min-width="220"
|
||||
@@ -148,11 +152,22 @@
|
||||
prop="arrivalPhone"
|
||||
label="收货联系人电话"
|
||||
width="140"
|
||||
/><el-table-column prop="startDate" label="开始时间" width="160" sortable /><el-table-column
|
||||
prop="endDate"
|
||||
label="结束时间"
|
||||
width="160"
|
||||
sortable
|
||||
/><el-table-column prop="cargoName" label="货物名称" width="130" /><el-table-column
|
||||
prop="cargoType"
|
||||
label="货物类型"
|
||||
width="120"
|
||||
/><el-table-column prop="packageType" label="包装" width="100" /><el-table-column
|
||||
prop="quantity"
|
||||
label="数量"
|
||||
width="90"
|
||||
/><el-table-column prop="quantityUnit" label="数量单位" width="100" /><el-table-column
|
||||
prop="specification"
|
||||
label="规格"
|
||||
width="120"
|
||||
/><el-table-column prop="model" label="型号" width="120" /><el-table-column
|
||||
prop="mileage"
|
||||
label="里程(km)"
|
||||
width="100"
|
||||
/><el-table-column prop="unitPrice" label="单价" width="90" /><el-table-column
|
||||
prop="freight"
|
||||
label="运费"
|
||||
@@ -161,11 +176,31 @@
|
||||
prop="freightTotal"
|
||||
label="运费合计"
|
||||
width="100"
|
||||
/><el-table-column
|
||||
prop="actualStartDate"
|
||||
label="实际发货时间"
|
||||
width="160"
|
||||
sortable
|
||||
/><el-table-column
|
||||
prop="actualEndDate"
|
||||
label="实际完成时间"
|
||||
width="160"
|
||||
sortable
|
||||
/><el-table-column
|
||||
prop="planStartDate"
|
||||
label="预计发货时间"
|
||||
width="160"
|
||||
sortable
|
||||
/><el-table-column
|
||||
prop="planEndDate"
|
||||
label="预计完成时间"
|
||||
width="160"
|
||||
sortable
|
||||
/><el-table-column prop="remark" label="备注" min-width="160" /><el-table-column
|
||||
label="操作"
|
||||
width="80"
|
||||
>-</el-table-column
|
||||
></el-table
|
||||
prop="waybillIdentifier"
|
||||
label="同一运单标识号"
|
||||
min-width="140"
|
||||
/><el-table-column label="操作" width="80">-</el-table-column></el-table
|
||||
>
|
||||
<el-pagination
|
||||
v-model:current-page="detailPage.current"
|
||||
@@ -441,7 +476,6 @@
|
||||
</section>
|
||||
<div v-if="createPage" class="waybill-import-create__footer">
|
||||
<el-button @click="closeCreate">取消</el-button>
|
||||
<el-button @click="saveDraft">保存草稿</el-button>
|
||||
<el-button type="primary" @click="confirmImport">确认导入</el-button>
|
||||
</div>
|
||||
</component>
|
||||
@@ -496,7 +530,7 @@ const createDefaultForm = () => ({
|
||||
const form = reactive(createDefaultForm());
|
||||
// 批量导入状态仅保留草稿与导入完成两种,与后端 importStatus 取值一致。
|
||||
const importStatusOptions = [
|
||||
{ label: '草稿', value: 'draft' },
|
||||
{ label: '进行中', value: 'draft' },
|
||||
{ label: '导入完成', value: 'completed' },
|
||||
];
|
||||
// 编辑草稿时明细已入库,此时不再强制重新上传附件。
|
||||
@@ -541,7 +575,7 @@ const previewRows = computed(() =>
|
||||
);
|
||||
const carrierContractsLoaded = ref(false),
|
||||
hasCarrierContracts = ref(null);
|
||||
const carrierTypes = ['承运商', '自运', '网货平台'];
|
||||
const carrierTypes = ['承运商', '自运'];
|
||||
const isSelfOperated = computed(() => form.carrierType === '自运');
|
||||
// 项目下没有承运商合同时只允许自运,与运单管理的承运类型收窄规则一致。
|
||||
const carrierTypeOptions = computed(() =>
|
||||
@@ -570,19 +604,47 @@ const resetCreateForm = () => {
|
||||
const detailColumns = [
|
||||
{ prop: 'batchNo', label: '运单批次号', minWidth: 150 },
|
||||
{ prop: 'originalNo', label: '原始单号', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'loadingIdentifier', label: '配载标识号', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'vehicleNo', label: '车牌号/航班号/船号/班列号', editor: 'input', minWidth: 220 },
|
||||
{ prop: 'driverName', label: '司机/船长', editor: 'driver', minWidth: 170 },
|
||||
{ prop: 'transportType', label: '运输类型', editor: 'transportType', minWidth: 150 },
|
||||
{ prop: 'transportType', label: '运输方式', editor: 'transportType', minWidth: 150 },
|
||||
{ prop: 'driverName', label: '司机/船长姓名', editor: 'driver', minWidth: 170 },
|
||||
{ prop: 'driverPhone', label: '司机/船长手机号', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'departureAddress', label: '发货地址', editor: 'input', minWidth: 220 },
|
||||
{ prop: 'departureContact', label: '发货联系人', editor: 'input', minWidth: 120 },
|
||||
{ prop: 'departurePhone', label: '发货联系人电话', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'arrivalAddress', label: '到货地址', editor: 'input', minWidth: 220 },
|
||||
{ prop: 'arrivalContact', label: '收货联系人', editor: 'input', minWidth: 120 },
|
||||
{ prop: 'arrivalPhone', label: '收货联系人电话', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'cargoName', label: '货物名称', editor: 'cargoName', minWidth: 220 },
|
||||
{ prop: 'cargoType', label: '货物类型', editor: 'cargoType', minWidth: 220 },
|
||||
{ prop: 'quantity', label: '重量', editor: 'number', minWidth: 130 },
|
||||
{ prop: 'packageType', label: '包装', editor: 'input', minWidth: 120 },
|
||||
{ prop: 'quantity', label: '数量', editor: 'number', minWidth: 130 },
|
||||
{ prop: 'quantityUnit', label: '数量单位', editor: 'input', minWidth: 120 },
|
||||
{ prop: 'specification', label: '规格', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'model', label: '型号', editor: 'input', minWidth: 150 },
|
||||
{ prop: 'mileage', label: '里程(km)', editor: 'number', minWidth: 120 },
|
||||
{ prop: 'unitPrice', label: '单价', editor: 'number', minWidth: 120 },
|
||||
{ prop: 'freight', label: '运费', editor: 'number', minWidth: 120 },
|
||||
{ prop: 'otherFeeTotal', label: '其他费用合计', editor: 'number', minWidth: 140 },
|
||||
{ prop: 'freightTotal', label: '运费合计', editor: 'number', minWidth: 120 },
|
||||
{ prop: 'actualStartDate', label: '实际发货时间', editor: 'date', minWidth: 160 },
|
||||
{ prop: 'actualEndDate', label: '实际完成时间', editor: 'date', minWidth: 160 },
|
||||
{ prop: 'planStartDate', label: '预计发货时间', editor: 'date', minWidth: 160 },
|
||||
{ prop: 'planEndDate', label: '预计完成时间', editor: 'date', minWidth: 160 },
|
||||
{ prop: 'remark', label: '备注', editor: 'textarea', minWidth: 200 },
|
||||
{ prop: 'waybillIdentifier', label: '同一运单标识号', editor: 'input', minWidth: 150 },
|
||||
];
|
||||
const requiredDetailFields = [
|
||||
{ prop: 'vehicleNo', label: '车牌号/航班号/船号/班列号' },
|
||||
{ prop: 'driverName', label: '司机/船长' },
|
||||
{ prop: 'transportType', label: '运输类型' },
|
||||
{ prop: 'transportType', label: '运输方式' },
|
||||
{ prop: 'departureAddress', label: '发货地址' },
|
||||
{ prop: 'arrivalAddress', label: '到货地址' },
|
||||
{ prop: 'cargoName', label: '货物名称' },
|
||||
{ prop: 'cargoType', label: '货物类型' },
|
||||
{ prop: 'quantity', label: '数量' },
|
||||
{ prop: 'quantityUnit', label: '数量单位' },
|
||||
{ prop: 'actualStartDate', label: '实际发货时间' },
|
||||
{ prop: 'actualEndDate', label: '实际完成时间' },
|
||||
];
|
||||
const projectQueryParams = { approvalStatuses: 'approved,change_approved' };
|
||||
const extractRecords = res => {
|
||||
@@ -975,6 +1037,12 @@ const carrierChange = value => {
|
||||
if (!isSelfOperated.value) form.carrierContractId = item.carrierContractId || '';
|
||||
};
|
||||
const fileChange = async (file, list) => {
|
||||
if (file.raw?.size > 50 * 1024 * 1024) {
|
||||
ElMessage.warning('单个文件大小不能超过50M');
|
||||
files.value = [];
|
||||
form.file = null;
|
||||
return;
|
||||
}
|
||||
files.value = list.slice(-1);
|
||||
form.file = file.raw;
|
||||
const XLSX = await import('xlsx');
|
||||
@@ -988,25 +1056,35 @@ const fileChange = async (file, list) => {
|
||||
);
|
||||
const keyMap = {
|
||||
originalNo: ['原始单号'],
|
||||
loadingIdentifier: ['配载标识号'],
|
||||
vehicleNo: ['车牌号/航班号/船号/班列号'],
|
||||
driverName: ['司机/船长'],
|
||||
transportType: ['运输类型', '运输方式'],
|
||||
cargoName: ['货物名称'],
|
||||
cargoType: ['货物类型'],
|
||||
quantity: ['重量'],
|
||||
transportType: ['运输方式', '运输类型'],
|
||||
driverName: ['司机/船长姓名', '司机/船长'],
|
||||
driverPhone: ['司机/船长手机号'],
|
||||
departureAddress: ['发货地址'],
|
||||
departureContact: ['发货联系人'],
|
||||
departurePhone: ['发货联系人电话'],
|
||||
arrivalAddress: ['到货地址'],
|
||||
arrivalContact: ['到货联系人', '收货联系人'],
|
||||
arrivalContact: ['收货联系人', '到货联系人'],
|
||||
arrivalPhone: ['收货联系人电话'],
|
||||
startDate: ['开始时间'],
|
||||
endDate: ['结束时间'],
|
||||
cargoName: ['货物名称'],
|
||||
cargoType: ['货物类型'],
|
||||
packageType: ['包装'],
|
||||
quantity: ['数量', '重量'],
|
||||
quantityUnit: ['数量单位'],
|
||||
specification: ['规格'],
|
||||
model: ['型号'],
|
||||
mileage: ['里程(km)', '里程'],
|
||||
unitPrice: ['单价'],
|
||||
freight: ['运费'],
|
||||
otherFeeTotal: ['其他费用合计'],
|
||||
freightTotal: ['运费合计'],
|
||||
actualStartDate: ['实际发货时间', '开始时间'],
|
||||
actualEndDate: ['实际完成时间', '结束时间'],
|
||||
planStartDate: ['预计发货时间'],
|
||||
planEndDate: ['预计完成时间'],
|
||||
remark: ['备注'],
|
||||
waybillIdentifier: ['同一运单标识号'],
|
||||
};
|
||||
const normalizeImportDate = value => {
|
||||
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
||||
@@ -1025,8 +1103,10 @@ const fileChange = async (file, list) => {
|
||||
item[key] ??
|
||||
'';
|
||||
});
|
||||
row.startDate = normalizeImportDate(row.startDate);
|
||||
row.endDate = normalizeImportDate(row.endDate);
|
||||
row.actualStartDate = normalizeImportDate(row.actualStartDate);
|
||||
row.actualEndDate = normalizeImportDate(row.actualEndDate);
|
||||
row.planStartDate = normalizeImportDate(row.planStartDate);
|
||||
row.planEndDate = normalizeImportDate(row.planEndDate);
|
||||
const duplicateKey = JSON.stringify(Object.keys(keyMap).map(key => row[key]));
|
||||
count.set(duplicateKey, (count.get(duplicateKey) || 0) + 1);
|
||||
row._duplicateKey = duplicateKey;
|
||||
@@ -1143,6 +1223,9 @@ const normalizeImportRow = row => ({
|
||||
otherFeeTotal: normalizeOptionalImportNumber(
|
||||
firstNotEmpty(row.otherFeeTotal, row['其他费用合计'])
|
||||
),
|
||||
unitPrice: normalizeOptionalImportNumber(firstNotEmpty(row.unitPrice, row['单价'])),
|
||||
freight: normalizeOptionalImportNumber(firstNotEmpty(row.freight, row['运费'])),
|
||||
freightTotal: normalizeOptionalImportNumber(firstNotEmpty(row.freightTotal, row['运费合计'])),
|
||||
transportType: firstNotEmpty(
|
||||
row.transportType,
|
||||
row['运输类型'],
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
'waybill-manage-page',
|
||||
{
|
||||
'waybill-manage-page--form-page': isStandaloneWaybillFormPage,
|
||||
'waybill-manage-page--detail-page': isStandaloneWaybillDetailPage,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<component
|
||||
v-if="!isStandaloneWaybillDetailPage"
|
||||
:is="crudContainer"
|
||||
:option="pageFormOption"
|
||||
:form-page-title="isStandaloneWaybillFormPage ? formPageTitle : undefined"
|
||||
@@ -381,7 +383,7 @@
|
||||
>
|
||||
<span>货物信息</span>
|
||||
<div v-if="!dialogReadonly" class="waybill-manage-page__section-actions">
|
||||
<el-link type="primary" @click="cargoImportBox = true">导入货物</el-link>
|
||||
<!-- <el-link type="primary" @click="cargoImportBox = true">导入货物</el-link> -->
|
||||
<el-link type="primary" @click="openCommonCargoDialog">常用货物</el-link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1514,7 +1516,7 @@
|
||||
v-model="attachmentRows"
|
||||
:readonly="dialogReadonly"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:max-size="50"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleAttachmentChange"
|
||||
@@ -1653,24 +1655,29 @@
|
||||
</el-dialog>
|
||||
|
||||
<empty-pagination
|
||||
v-show="!isStandaloneWaybillFormPage"
|
||||
v-show="!isStandaloneWaybillFormPage && !isStandaloneWaybillDetailPage"
|
||||
:page="page"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@load="onLoad(page, query)"
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
<component
|
||||
:is="detailContainer"
|
||||
v-model="detailBox"
|
||||
:title="`${config.title}详情`"
|
||||
append-to-body
|
||||
:append-to-body="!isStandaloneWaybillDetailPage"
|
||||
top="10px"
|
||||
width="96%"
|
||||
show-close
|
||||
:class="['waybill-manage-page__detail-dialog', $attrs.option?.dialogCustomClass]"
|
||||
:class="[
|
||||
'waybill-manage-page__detail-dialog',
|
||||
{ 'waybill-manage-page__detail-page': isStandaloneWaybillDetailPage },
|
||||
$attrs.option?.dialogCustomClass,
|
||||
]"
|
||||
>
|
||||
<div v-loading="detailLoading" class="waybill-manage-page__detail-content">
|
||||
<template v-if="detailBox">
|
||||
<template v-if="detailBox || isStandaloneWaybillDetailPage">
|
||||
<section-card class="waybill-manage-page__waybill-detail-summary">
|
||||
<div class="waybill-manage-page__waybill-heading">
|
||||
<strong>运单详情</strong>
|
||||
@@ -1775,7 +1782,6 @@
|
||||
transportCargoRows.length ? transportCargoRows : parseJsonArray(detailRow.goodsJson)
|
||||
"
|
||||
border
|
||||
height="220"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="70" />
|
||||
<el-table-column prop="cargoName" label="货物名称" min-width="150" />
|
||||
@@ -1982,10 +1988,13 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="detailBox = false">关闭</el-button>
|
||||
<div v-if="isStandaloneWaybillDetailPage" class="waybill-manage-page__detail-footer">
|
||||
<el-button type="primary" @click="closeDetail">关闭</el-button>
|
||||
</div>
|
||||
<template v-if="!isStandaloneWaybillDetailPage" #footer>
|
||||
<el-button type="primary" @click="closeDetail">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</component>
|
||||
|
||||
<el-dialog
|
||||
v-model="attachmentDocumentPreviewVisible"
|
||||
@@ -2527,7 +2536,10 @@
|
||||
搜索
|
||||
</el-button>
|
||||
</div>
|
||||
<div ref="transportAmap" class="waybill-manage-page__map"></div>
|
||||
<div class="map-picker-content">
|
||||
<map-search-results :results="transportMapSearchResults" @select="selectTransportMapSearchResult" />
|
||||
<div ref="transportAmap" class="waybill-manage-page__map"></div>
|
||||
</div>
|
||||
<div class="waybill-manage-page__map-info">
|
||||
<span>{{ transportMapStatus }}</span>
|
||||
<span v-if="transportMapSelected.regionName"
|
||||
@@ -2815,6 +2827,7 @@ const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
||||
let amapLoader;
|
||||
const standaloneWaybillFormRoute = '/business/waybill-manage/form';
|
||||
const standaloneWaybillFormPaths = ['/business/waybill-manage', standaloneWaybillFormRoute];
|
||||
const standaloneWaybillDetailRoute = '/business/waybill-manage/detail';
|
||||
const loadingCreateWaybillsStorageKey = 'loading-manage-create-waybills';
|
||||
|
||||
const attachmentViewerPlugins = [
|
||||
@@ -2870,6 +2883,16 @@ const PageAvueForm = {
|
||||
},
|
||||
};
|
||||
|
||||
const PageDetail = {
|
||||
inheritAttrs: false,
|
||||
render() {
|
||||
return h('div', { class: this.$attrs.class }, [
|
||||
...(this.$slots.default?.() || []),
|
||||
...(this.$slots.footer?.() || []),
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
||||
const estimateMenuButtonCount = config => {
|
||||
const rowActions = (config.actions || []).filter(action => action !== 'batchComplete');
|
||||
return 3 + rowActions.length + (config.operations || []).length;
|
||||
@@ -2905,7 +2928,7 @@ const defaultTransportCargo = () => ({
|
||||
remark: '',
|
||||
});
|
||||
|
||||
const taskCarrierTypes = ['承运商', '自运', '网货平台'];
|
||||
const taskCarrierTypes = ['承运商', '自运'];
|
||||
|
||||
// Avue 默认分组使用折叠容器。独立运单表单需要始终展示各业务分区,
|
||||
// 因此仅替换本页面的分组渲染器,保留 avue-form 的字段渲染和校验能力。
|
||||
@@ -2955,6 +2978,7 @@ const getPlainAvueForm = avueForm => {
|
||||
export default {
|
||||
components: {
|
||||
PageAvueForm,
|
||||
PageDetail,
|
||||
InfoFilled,
|
||||
Rank,
|
||||
ElImageViewer,
|
||||
@@ -2991,6 +3015,10 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
standaloneDetailPage: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -3144,6 +3172,7 @@ export default {
|
||||
transportMapKeyword: '',
|
||||
transportMapStatus: '可搜索地址或点击地图选点',
|
||||
transportMapSelected: {},
|
||||
transportMapSearchResults: [],
|
||||
transportAmap: null,
|
||||
transportAmapMarker: null,
|
||||
transportAmapGeocoder: null,
|
||||
@@ -3250,6 +3279,12 @@ export default {
|
||||
isStandaloneWaybillFormPage() {
|
||||
return this.isStandaloneWaybillPage && ['add', 'edit'].includes(this.$route.query.mode);
|
||||
},
|
||||
isStandaloneWaybillDetailPage() {
|
||||
return this.standaloneDetailPage && this.$route.path === standaloneWaybillDetailRoute;
|
||||
},
|
||||
detailContainer() {
|
||||
return this.isStandaloneWaybillDetailPage ? 'PageDetail' : 'el-dialog';
|
||||
},
|
||||
formPageTitle() {
|
||||
if (this.isStandaloneWaybillFormPage) {
|
||||
return (
|
||||
@@ -3338,7 +3373,7 @@ export default {
|
||||
return this.taskCarrierTypes;
|
||||
},
|
||||
taskCarrierLabel() {
|
||||
return this.form.carrierType === '网货平台' ? '承运方' : '承运商';
|
||||
return '承运商';
|
||||
},
|
||||
taskCarrierValue: {
|
||||
get() {
|
||||
@@ -3533,10 +3568,16 @@ export default {
|
||||
if (!this.isStandaloneWaybillFormPage) return;
|
||||
const mode = this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
||||
const id = this.$route.query.id || '';
|
||||
const key = `${mode}:${id}`;
|
||||
const copyId = this.$route.query.copyId || '';
|
||||
const key = `${mode}:${id}:${copyId}`;
|
||||
if (this.standaloneFormKey === key) return;
|
||||
this.standaloneFormKey = key;
|
||||
if (mode === 'edit') this.form = { id };
|
||||
if (mode === 'edit') {
|
||||
this.form = { id };
|
||||
} else if (mode === 'add' && copyId) {
|
||||
// 复制模式:加载原数据
|
||||
this.form = { id: copyId };
|
||||
}
|
||||
this.beforeOpen(() => {}, mode);
|
||||
},
|
||||
buildFormGroupOption(option) {
|
||||
@@ -4034,7 +4075,8 @@ export default {
|
||||
waybillUnitPrice(row = {}) {
|
||||
const price = row.unitPrice || row.price || '';
|
||||
const unit = row.priceUnit || row.billingUnit || '';
|
||||
return price === '' ? '' : unit ? `${price}(${unit})` : price;
|
||||
if (price === '' || Number(price) === -1) return '';
|
||||
return unit ? `${price}(${unit})` : price;
|
||||
},
|
||||
waybillCurrencyRemark(row = {}) {
|
||||
const freight = this.parseJsonObject(row.freightJson);
|
||||
@@ -4066,6 +4108,10 @@ export default {
|
||||
return { value: `${this.waybillCurrencyRemark(row)}${value}` };
|
||||
},
|
||||
openDetail(row) {
|
||||
if (!this.isStandaloneWaybillDetailPage) {
|
||||
this.$router.push({ path: standaloneWaybillDetailRoute, query: { id: row.id } });
|
||||
return;
|
||||
}
|
||||
this.detailBox = true;
|
||||
this.detailLoading = true;
|
||||
this.detailRow = { ...row };
|
||||
@@ -4088,6 +4134,8 @@ export default {
|
||||
this.detailRow = {
|
||||
...detail,
|
||||
contractNo: contract.contractNo || detail.contractNo || detail.contractName || '',
|
||||
customerName:
|
||||
contract.partyA || contract.customerName || detail.customerName || '',
|
||||
};
|
||||
this.applyFormDetail(this.detailRow);
|
||||
return this.loadWaybillDetailProcessNodes(this.detailRow.projectId);
|
||||
@@ -4108,6 +4156,14 @@ export default {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
closeDetail() {
|
||||
if (this.isStandaloneWaybillDetailPage) {
|
||||
this.$router.$avueRouter?.closeTag?.();
|
||||
this.$router.push({ path: '/business/waybill-manage', query: {} });
|
||||
return;
|
||||
}
|
||||
this.detailBox = false;
|
||||
},
|
||||
async openLoadingDetail(row = {}) {
|
||||
let id = row.loadingId || row.loadingManageId || '';
|
||||
if (!id && row.loadingNo) {
|
||||
@@ -4744,6 +4800,30 @@ export default {
|
||||
beforeOpen(done, type) {
|
||||
this.crudDialogType = type;
|
||||
this.dialogReadonly = type === 'view';
|
||||
const copyId = this.$route.query.copyId || '';
|
||||
|
||||
// 复制模式:先加载原数据,然后清除 ID 相关字段
|
||||
if (type === 'add' && copyId) {
|
||||
this.api.getDetail(copyId).then(res => {
|
||||
const sourceData = res.data.data || {};
|
||||
// 清除不应该复制的字段
|
||||
delete sourceData.id;
|
||||
delete sourceData.code;
|
||||
delete sourceData.waybillStatus;
|
||||
delete sourceData.createTime;
|
||||
delete sourceData.updateTime;
|
||||
delete sourceData.createUser;
|
||||
delete sourceData.updateUser;
|
||||
delete sourceData.createDept;
|
||||
|
||||
this.applyFormDetail(sourceData);
|
||||
// 标记为手工创建
|
||||
this.form.dataSource = '手工创建';
|
||||
done();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'add') {
|
||||
this.form = {
|
||||
...(this.config.defaultForm || {}),
|
||||
@@ -4998,7 +5078,11 @@ export default {
|
||||
this.form.settlementCurrency =
|
||||
contract.settlementCurrency || contract.settlementCurrencyCode || contract.currency || '';
|
||||
this.form.customerName =
|
||||
contract.customerName || contract.customerNames || this.form.customerName || '';
|
||||
contract.partyA ||
|
||||
contract.customerName ||
|
||||
contract.customerNames ||
|
||||
this.form.customerName ||
|
||||
'';
|
||||
this.syncTaskFreightCurrencyFromContract(contract);
|
||||
this.fillSelfOperatedCarrierFromContract(contract);
|
||||
this.$refs.crud?.validateField?.('contractName');
|
||||
@@ -5409,7 +5493,7 @@ export default {
|
||||
this.form.carrierId = '';
|
||||
},
|
||||
isTaskCarrierRequired(carrierType) {
|
||||
return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
|
||||
return String(carrierType || '').trim() === '承运商';
|
||||
},
|
||||
taskCarrierOptionValue(item = {}) {
|
||||
if (this.form.carrierType !== '自运') {
|
||||
@@ -5986,7 +6070,7 @@ export default {
|
||||
['vehicleNo', this.transportVehicleNoLabel],
|
||||
];
|
||||
if (this.isTaskCarrierRequired(row.carrierType)) {
|
||||
requiredFields.push(['carrierName', row.carrierType === '网货平台' ? '承运方' : '承运商']);
|
||||
requiredFields.push(['carrierName', '承运商']);
|
||||
}
|
||||
if (row.carrierType === '承运商') {
|
||||
requiredFields.push(['carrierContractId', '承运商合同']);
|
||||
@@ -6805,6 +6889,12 @@ export default {
|
||||
this.ensureTransportAmapGeocoder()
|
||||
.then(() => this.runAmapGeocode('location', keyword))
|
||||
.then(result => {
|
||||
this.transportMapSearchResults = (result.geocodes || []).map((item, index) => ({
|
||||
id: item.id || index,
|
||||
name: item.formattedAddress || keyword,
|
||||
address: item.formattedAddress || keyword,
|
||||
location: item.location,
|
||||
}));
|
||||
const point = this.resolveMapPoint(result);
|
||||
if (!point) {
|
||||
this.transportMapStatus = '未找到匹配地址';
|
||||
@@ -6825,6 +6915,11 @@ export default {
|
||||
this.$message.error('高德地图组件加载失败,请稍后重试或重新打开弹窗');
|
||||
});
|
||||
},
|
||||
selectTransportMapSearchResult(item) {
|
||||
if (item && item.location) {
|
||||
this.pickTransportMapPoint(item.location, item.address || item.name || '');
|
||||
}
|
||||
},
|
||||
pickTransportMapPoint(lnglat, keyword) {
|
||||
const longitude = this.getPointLng(lnglat);
|
||||
const latitude = this.getPointLat(lnglat);
|
||||
@@ -7559,6 +7654,18 @@ export default {
|
||||
openImportDialog(this, this.config.title, () => this.onLoad(this.page, this.query));
|
||||
},
|
||||
handleCopy(row) {
|
||||
if (this.isStandaloneWaybillPage) {
|
||||
this.$router.push({
|
||||
path: standaloneWaybillFormRoute,
|
||||
query: {
|
||||
mode: 'add',
|
||||
copyId: row.id,
|
||||
name: `复制${this.config.title || ''}`,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 非独立页面模式,使用原有的弹窗复制逻辑
|
||||
this.$confirm(`确定复制该${this.config.title}?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
@@ -8629,6 +8736,17 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
&__detail-page {
|
||||
padding: 12px;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
|
||||
&__detail-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
&__waybill-detail-summary {
|
||||
:deep(.el-card__body) {
|
||||
padding: 18px 22px;
|
||||
@@ -8835,10 +8953,6 @@ export default {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
|
||||
.section-card {
|
||||
min-height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
&__waybill-map-empty {
|
||||
|
||||
Reference in New Issue
Block a user