1、调整凭证
2、调整运单
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user