1、新增应付明细
2、新增应收明细 3、新增异常处置 4、新增风险处置 5、新增在途追踪 6、修复业务模块bug
This commit is contained in:
@@ -49,13 +49,16 @@
|
||||
<section>
|
||||
<div class="section-head">
|
||||
<h3>收发货信息</h3>
|
||||
<el-link type="primary" @click="openRouteDialog">选择线路</el-link>
|
||||
<div class="section-head__actions">
|
||||
<el-link type="primary" @click="addRoute">新增途经点</el-link>
|
||||
<el-link type="primary" @click="openRouteDialog">选择线路</el-link>
|
||||
</div>
|
||||
</div>
|
||||
<el-form :model="form" label-position="right" label-width="auto"
|
||||
><div class="route-steps">
|
||||
<el-steps direction="vertical" :active="form.routes.length + 1">
|
||||
<el-step>
|
||||
<template #icon><span class="route-step-icon">起</span></template>
|
||||
<template #icon><span class="route-step-icon route-step-icon--start">起</span></template>
|
||||
<template #description>
|
||||
<div class="route-node-form">
|
||||
<el-form-item label="发货地址" required class="route-address-form-item"
|
||||
@@ -80,7 +83,7 @@
|
||||
</template>
|
||||
</el-step>
|
||||
<el-step v-for="(route, index) in form.routes" :key="index">
|
||||
<template #icon><span class="route-step-icon">经</span></template>
|
||||
<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 #description>
|
||||
<div class="route-node-form">
|
||||
@@ -89,12 +92,12 @@
|
||||
><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-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 v-if="form.routes.length > 1" content="删除途经点" placement="top"><el-button class="route-action-button" type="danger" link @click="removeRoute(index)"><el-icon><Remove /></el-icon></el-button></el-tooltip></div></el-form-item>
|
||||
<el-form-item label="联系方式"><div class="route-contact-actions"><el-input v-model="route.departurePhone" placeholder="请输入" /><el-tooltip v-if="index === form.routes.length - 1" content="新增途经点" placement="top"><el-button class="route-action-button" type="primary" link @click="addRoute"><el-icon><CirclePlus /></el-icon></el-button></el-tooltip><el-tooltip content="删除途经点" placement="top"><el-button class="route-action-button" type="danger" link @click="removeRoute(index)"><el-icon><Remove /></el-icon></el-button></el-tooltip></div></el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
<el-step>
|
||||
<template #icon><span class="route-step-icon">终</span></template>
|
||||
<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 #description>
|
||||
<div class="route-node-form">
|
||||
@@ -155,12 +158,13 @@
|
||||
><el-input
|
||||
v-model="row.cargoType"
|
||||
placeholder="从货物类型获取" /></template></el-table-column
|
||||
><el-table-column label="数量" width="104" class-name="master-editor__quantity-cell"
|
||||
><el-table-column label="数量" width="132" class-name="master-editor__quantity-cell"
|
||||
><template #default="{ row }"
|
||||
><el-input-number
|
||||
><el-input
|
||||
v-model="row.quantity"
|
||||
:min="0"
|
||||
controls-position="right" /></template></el-table-column
|
||||
inputmode="decimal"
|
||||
placeholder="请输入"
|
||||
@input="value => handleQuantityInput(row, value)" /></template></el-table-column
|
||||
><el-table-column label="数量单位" width="120"
|
||||
><template #default="{ row }"
|
||||
><el-select v-model="row.quantityUnit"
|
||||
@@ -200,13 +204,49 @@
|
||||
</section>
|
||||
<section>
|
||||
<h3>附件</h3>
|
||||
<el-upload
|
||||
multiple
|
||||
action="/api/blade-resource/oss/endpoint/put-file"
|
||||
:headers="uploadHeaders"
|
||||
:on-success="attach"
|
||||
><el-button type="primary" plain>上传附件</el-button></el-upload
|
||||
>
|
||||
<div class="master-editor__attachment">
|
||||
<div class="master-editor__attachment-head">
|
||||
<vehicle-attachment-upload
|
||||
v-model="attachmentRows"
|
||||
:file-types="attachmentFileTypes"
|
||||
:max-size="500"
|
||||
:show-tip="false"
|
||||
:show-file-list="false"
|
||||
button-text="上传附件"
|
||||
@change="handleAttachmentChange"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="!attachmentRows.length"
|
||||
@click="handleAttachmentBatchDownload"
|
||||
>
|
||||
批量下载
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="attachmentRows"
|
||||
border
|
||||
class="master-editor__attachment-table"
|
||||
@selection-change="handleAttachmentSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||
<el-table-column label="文件名" min-width="240" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.originalName || row.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" width="120" align="center">
|
||||
<template #default="{ row }">{{ formatFileSize(row.size) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="uploadUserName" label="上传人" width="140" align="center" />
|
||||
<el-table-column prop="uploadTime" label="上传时间" width="170" align="center" sortable />
|
||||
<el-table-column label="操作" width="120" align="center" fixed="right">
|
||||
<template #default="{ row, $index }">
|
||||
<el-link type="danger" @click="removeAttachment($index)">删除</el-link>
|
||||
<el-link type="primary" @click="downloadAttachment(row)">下载</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</section>
|
||||
<el-dialog
|
||||
v-model="addressDialogVisible"
|
||||
@@ -402,9 +442,9 @@ 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 { exportBlob, importBlob } from '@/api/common';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import { getUploadHeaders } from '@/utils/upload';
|
||||
import { List, Location, Search } from '@element-plus/icons-vue';
|
||||
import { downloadFileByUrl, downloadXls } from '@/utils/util';
|
||||
import { List, Search } from '@element-plus/icons-vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
|
||||
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
|
||||
@@ -455,7 +495,24 @@ export default {
|
||||
cargoOptions: [],
|
||||
cargoImportVisible: false,
|
||||
cargoImportLoading: false,
|
||||
uploadHeaders: getUploadHeaders(),
|
||||
attachmentRows: [],
|
||||
selectedAttachmentRows: [],
|
||||
attachmentFileTypes: [
|
||||
'pdf',
|
||||
'bmp',
|
||||
'jpeg',
|
||||
'png',
|
||||
'jpg',
|
||||
'doc',
|
||||
'docx',
|
||||
'ppt',
|
||||
'pptx',
|
||||
'xlsx',
|
||||
'xls',
|
||||
'eml',
|
||||
'msg',
|
||||
'zip',
|
||||
],
|
||||
transports: ['公路运输', '铁路运输', '水路运输', '航空运输'],
|
||||
quantityUnitOptions,
|
||||
form: {
|
||||
@@ -471,6 +528,7 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
lastRouteName() {
|
||||
return this.form.routes[this.form.routes.length - 1]?.departureName || '途经地';
|
||||
},
|
||||
@@ -513,10 +571,23 @@ export default {
|
||||
? finalSegment.transportType
|
||||
: detail.finalTransportType || '',
|
||||
goods: detail.goods || [{ quantityUnit: quantityUnitOptions[0] }],
|
||||
attachmentsJson: detail.attachmentsJson || '[]',
|
||||
transportOrganizationType: '多式联运',
|
||||
};
|
||||
this.attachmentRows = this.parseJsonArray(this.form.attachmentsJson);
|
||||
this.selectedAttachmentRows = [];
|
||||
if (this.form.projectId) await this.loadContracts(this.form.projectId, false);
|
||||
},
|
||||
parseJsonArray(value) {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) return value;
|
||||
try {
|
||||
const data = JSON.parse(value);
|
||||
return Array.isArray(data) ? data : [];
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
getRouteTitle(index) {
|
||||
const departureName =
|
||||
index === 0
|
||||
@@ -980,7 +1051,7 @@ export default {
|
||||
this.form.routes.push(this.createRoute());
|
||||
},
|
||||
removeRoute(index) {
|
||||
if (this.form.routes.length > 1) this.form.routes.splice(index, 1);
|
||||
this.form.routes.splice(index, 1);
|
||||
},
|
||||
addGoods(index) {
|
||||
this.form.goods.splice(index + 1, 0, { quantityUnit: quantityUnitOptions[0] });
|
||||
@@ -988,10 +1059,56 @@ export default {
|
||||
removeGoods(index) {
|
||||
if (this.form.goods.length > 1) this.form.goods.splice(index, 1);
|
||||
},
|
||||
attach(response) {
|
||||
const list = JSON.parse(this.form.attachmentsJson || '[]');
|
||||
list.push(response.data || response);
|
||||
this.form.attachmentsJson = JSON.stringify(list);
|
||||
handleQuantityInput(row, value) {
|
||||
const text = String(value || '').replace(/[^\d.]/g, '');
|
||||
const [integer = '', ...decimals] = text.split('.');
|
||||
row.quantity = decimals.length ? `${integer}.${decimals.join('').slice(0, 3)}` : integer;
|
||||
},
|
||||
syncAttachmentsJson() {
|
||||
this.form.attachmentsJson = JSON.stringify(this.attachmentRows);
|
||||
},
|
||||
handleAttachmentChange(list) {
|
||||
const userName = this.userInfo?.realName || this.userInfo?.userName || '';
|
||||
const uploadTime = this.$dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
this.attachmentRows = (list || []).map(item => ({
|
||||
...item,
|
||||
description: item.description || '',
|
||||
uploadUserName: item.uploadUserName || userName,
|
||||
uploadTime: item.uploadTime || uploadTime,
|
||||
}));
|
||||
this.selectedAttachmentRows = [];
|
||||
this.syncAttachmentsJson();
|
||||
},
|
||||
handleAttachmentSelectionChange(rows) {
|
||||
this.selectedAttachmentRows = rows || [];
|
||||
},
|
||||
removeAttachment(index) {
|
||||
this.attachmentRows.splice(index, 1);
|
||||
this.selectedAttachmentRows = this.selectedAttachmentRows.filter(item =>
|
||||
this.attachmentRows.includes(item)
|
||||
);
|
||||
this.syncAttachmentsJson();
|
||||
},
|
||||
formatFileSize(size) {
|
||||
const number = Number(size);
|
||||
if (!number) return '';
|
||||
if (number < 1024) return `${number}B`;
|
||||
if (number < 1024 * 1024) return `${(number / 1024).toFixed(1)}KB`;
|
||||
return `${(number / 1024 / 1024).toFixed(1)}MB`;
|
||||
},
|
||||
downloadAttachment(row) {
|
||||
const url = row.url || row.link;
|
||||
if (!url) {
|
||||
this.$message.warning('附件地址为空');
|
||||
return;
|
||||
}
|
||||
downloadFileByUrl(url, row.originalName || row.name || '附件');
|
||||
},
|
||||
handleAttachmentBatchDownload() {
|
||||
const rows = this.selectedAttachmentRows.length
|
||||
? this.selectedAttachmentRows
|
||||
: this.attachmentRows;
|
||||
rows.forEach(row => this.downloadAttachment(row));
|
||||
},
|
||||
async save(draft = false) {
|
||||
if (!draft) {
|
||||
@@ -1001,7 +1118,6 @@ export default {
|
||||
if (!this.form.goods.some(item => item.cargoType && item.quantity > 0 && item.quantityUnit))
|
||||
return this.$message.warning('请至少填写一条有效货物');
|
||||
if (
|
||||
!this.form.routes.length ||
|
||||
this.form.routes.some(item => !item.departureName || !item.transportType) ||
|
||||
!this.form.finalTransportType
|
||||
)
|
||||
@@ -1009,6 +1125,7 @@ export default {
|
||||
}
|
||||
const payload = {
|
||||
...this.form,
|
||||
attachmentsJson: JSON.stringify(this.attachmentRows),
|
||||
routes: [
|
||||
...this.form.routes.map((route, index) => ({ ...route, segmentNo: `段${index + 1}` })),
|
||||
{
|
||||
@@ -1024,7 +1141,8 @@ export default {
|
||||
delete payload.finalTransportType;
|
||||
const res = await (draft ? api.saveDraft(payload) : api.submit(payload));
|
||||
this.$message.success('保存成功');
|
||||
return res.data || res;
|
||||
// 兼容 Axios 响应及 BladeX 常见的 data.data 两层响应结构。
|
||||
return res?.data?.data || res?.data || res;
|
||||
},
|
||||
async saveDraft() {
|
||||
await this.save(true);
|
||||
@@ -1036,7 +1154,12 @@ export default {
|
||||
},
|
||||
async createAndDispatch() {
|
||||
const data = await this.save();
|
||||
this.$emit('dispatch', data.id);
|
||||
const id = data?.id || data?.masterOrderId || data?.masterId;
|
||||
if (!id) {
|
||||
this.$message.error('保存成功但未获取到总单ID,无法进入调度');
|
||||
return;
|
||||
}
|
||||
this.$emit('dispatch', id);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1060,15 +1183,15 @@ export default {
|
||||
.goods-heading {
|
||||
display: flex;
|
||||
margin-bottom: 8px;
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
h3 {
|
||||
margin-right: auto;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.goods-toolbar {
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
gap: 8px;
|
||||
}
|
||||
.goods-actions {
|
||||
@@ -1076,8 +1199,31 @@ export default {
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
:deep(.master-editor__quantity-cell .el-input-number) {
|
||||
width: 80px;
|
||||
.master-editor__attachment {
|
||||
width: 100%;
|
||||
}
|
||||
.master-editor__attachment-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.master-editor__attachment-table {
|
||||
width: 100%;
|
||||
:deep(.el-table__header th) {
|
||||
background: #f5f7fa;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
}
|
||||
:deep(.el-table__cell) {
|
||||
border-color: #eff1f7;
|
||||
}
|
||||
:deep(.el-table__row:nth-child(even) td.el-table__cell) {
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
:deep(.master-editor__quantity-cell .el-input) {
|
||||
width: 108px;
|
||||
}
|
||||
.section-head {
|
||||
display: flex;
|
||||
@@ -1088,6 +1234,10 @@ export default {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.section-head__actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
.route-dialog__search {
|
||||
margin-bottom: 8px;
|
||||
padding: 12px 12px 4px;
|
||||
@@ -1138,6 +1288,12 @@ export default {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
&--middle {
|
||||
background: #67c23a;
|
||||
}
|
||||
&--end {
|
||||
background: #e6a23c;
|
||||
}
|
||||
}
|
||||
}
|
||||
.route-node-form {
|
||||
|
||||
Reference in New Issue
Block a user