Merge remote-tracking branch 'websoft/master'
# Conflicts: # src/views/business/project-apply.vue
This commit is contained in:
@@ -3020,6 +3020,10 @@ export default {
|
||||
Location,
|
||||
OfficeBuilding,
|
||||
Search,
|
||||
// 实例所属路由的路径快照,用于在 watcher / 跳转前判断当前路由是否还是自己的。
|
||||
// 必须放 data:Options API 的顺序是 data → computed → watch(immediate) → created,
|
||||
// 放 created 的话首个 immediate watcher 会读到空值,把首次加载也挡掉。
|
||||
routePathLocked: this.$route.path,
|
||||
form: {},
|
||||
suppressTransportTypeClear: false,
|
||||
query: {},
|
||||
@@ -3596,6 +3600,11 @@ export default {
|
||||
!this.dialogReadonly
|
||||
);
|
||||
},
|
||||
// 当前路由是否仍属于本实例(tab.js 按 fullPath 建实例,一个实例只对应一个 path)。
|
||||
// 实例被 keep-alive 缓存后 $route 已跑到别的页面时,不能用「当前页面的 id」去查本页数据。
|
||||
isOwnedRouteActive() {
|
||||
return this.$route.path === this.routePathLocked;
|
||||
},
|
||||
billingCargoTypeCascaderProps() {
|
||||
return {
|
||||
label: 'cargoName',
|
||||
@@ -3673,6 +3682,9 @@ export default {
|
||||
detailId: {
|
||||
immediate: true,
|
||||
handler(id) {
|
||||
// 宿主 view 传的是全局 $route.query.detailId,必须确认当前路由还是自己的,
|
||||
// 否则跳到别的页面后会把「别人的 id」灌进来触发详情请求。
|
||||
if (!this.isOwnedRouteActive) return;
|
||||
if (id !== undefined && id !== null && id !== '') {
|
||||
this.$nextTick(() => this.openDetail({ id }));
|
||||
}
|
||||
|
||||
@@ -199,13 +199,17 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="detailVisible" title="导入运单详情" width="90%" append-to-body>
|
||||
<el-form :inline="true" :model="detailQuery"
|
||||
><el-form-item label="车牌号/船号"
|
||||
<section-card class="waybill-search">
|
||||
<el-form :inline="true" :model="detailQuery"
|
||||
>
|
||||
<el-form-item label="车牌号/船号"
|
||||
><el-input v-model="detailQuery.vehicleNo" clearable /></el-form-item
|
||||
><el-form-item label="司机/船长姓名"
|
||||
><el-input v-model="detailQuery.driverName" clearable placeholder="请输入" /></el-form-item
|
||||
><el-form-item label="司机/船长姓名"
|
||||
><el-input v-model="detailQuery.driverName" clearable placeholder="请输入" /></el-form-item
|
||||
><el-button type="primary" @click="loadDetails">查询</el-button></el-form
|
||||
>
|
||||
>
|
||||
</section-card>
|
||||
|
||||
<el-table :data="details" border
|
||||
><el-table-column type="index" label="序号" width="65" /><el-table-column
|
||||
prop="batchNo"
|
||||
@@ -593,6 +597,7 @@ import { getList as getDriverList } from '@/api/transportCapacity/driver';
|
||||
import { getDictionary } from '@/api/system/dictbiz';
|
||||
import * as api from '@/api/business/waybill-manage';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
import SectionCard from '@/components/section-card/main.vue';
|
||||
|
||||
const props = defineProps({ modelValue: Boolean, standalone: Boolean, createPage: Boolean });
|
||||
const emit = defineEmits(['update:modelValue', 'closed']);
|
||||
@@ -1617,4 +1622,7 @@ const confirmImport = async () => {
|
||||
:global(.avue-layout--horizontal .waybill-import-create__footer) {
|
||||
left: 0;
|
||||
}
|
||||
.waybill-search .el-form-item{
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
:class="[
|
||||
'waybill-manage-page',
|
||||
{
|
||||
'waybill-manage-page--form-page': isStandaloneWaybillFormPage,
|
||||
'waybill-manage-page--detail-page': isStandaloneWaybillDetailPage,
|
||||
'waybill-manage-page--form-page': formPageLocked,
|
||||
'waybill-manage-page--detail-page': detailPageLocked,
|
||||
},
|
||||
]"
|
||||
>
|
||||
<component
|
||||
v-if="!isStandaloneWaybillDetailPage"
|
||||
v-if="!detailPageLocked"
|
||||
:is="crudContainer"
|
||||
:option="pageFormOption"
|
||||
:form-page-title="isStandaloneWaybillFormPage ? formPageTitle : undefined"
|
||||
:form-page-title="formPageLocked ? formPageTitle : undefined"
|
||||
:table-loading="loading"
|
||||
:data="data"
|
||||
v-model:page="page"
|
||||
v-model="form"
|
||||
:status="isStandaloneWaybillFormPage ? dialogReadonly : undefined"
|
||||
:status="formPageLocked ? dialogReadonly : undefined"
|
||||
ref="crud"
|
||||
:permission="permissionList"
|
||||
:before-open="beforeOpen"
|
||||
@@ -1546,7 +1546,7 @@
|
||||
</el-link>
|
||||
</div>
|
||||
<div
|
||||
v-if="isStandaloneWaybillFormPage"
|
||||
v-if="formPageLocked"
|
||||
class="waybill-manage-page__standalone-menu-actions"
|
||||
>
|
||||
<el-button class="waybill-manage-page__form-close" @click="closeCrudDialog">
|
||||
@@ -1747,7 +1747,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<empty-pagination
|
||||
v-show="!isStandaloneWaybillFormPage && !isStandaloneWaybillDetailPage"
|
||||
v-show="!formPageLocked && !detailPageLocked"
|
||||
:page="page"
|
||||
@size-change="sizeChange"
|
||||
@current-change="currentChange"
|
||||
@@ -1758,18 +1758,18 @@
|
||||
:is="detailContainer"
|
||||
v-model="detailBox"
|
||||
:title="`${config.title}详情`"
|
||||
:append-to-body="!isStandaloneWaybillDetailPage"
|
||||
:append-to-body="!detailPageLocked"
|
||||
top="10px"
|
||||
width="96%"
|
||||
show-close
|
||||
:class="[
|
||||
'waybill-manage-page__detail-dialog',
|
||||
{ 'waybill-manage-page__detail-page': isStandaloneWaybillDetailPage },
|
||||
{ 'waybill-manage-page__detail-page': detailPageLocked },
|
||||
$attrs.option?.dialogCustomClass,
|
||||
]"
|
||||
>
|
||||
<div v-loading="detailLoading" class="waybill-manage-page__detail-content">
|
||||
<template v-if="detailBox || isStandaloneWaybillDetailPage">
|
||||
<template v-if="detailBox || detailPageLocked">
|
||||
<section-card class="waybill-manage-page__waybill-detail-summary">
|
||||
<div class="waybill-manage-page__waybill-heading">
|
||||
<strong>运单详情</strong>
|
||||
@@ -1923,6 +1923,12 @@
|
||||
}}</strong
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
v-if="!waybillIsCarrier(detailRow)"
|
||||
class="waybill-manage-page__waybill-detail-field"
|
||||
>
|
||||
<span>里程(km)</span><strong>{{ detailRow.mileage || '-' }}</strong>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="waybillIsNonRoadTransport(detailRow)">
|
||||
<div class="waybill-manage-page__waybill-detail-field">
|
||||
@@ -2029,8 +2035,8 @@
|
||||
<div v-loading="waybillPunchRecordsLoading">
|
||||
<el-timeline v-if="waybillPunchRecords.length">
|
||||
<el-timeline-item
|
||||
v-for="(record, index) in waybillPunchRecords"
|
||||
:key="record.nodeCode || record.id || index"
|
||||
v-for="(record, index) in orderedWaybillPunchRecords"
|
||||
:key="`${record.nodeCode || record.id || 'punch'}-${index}`"
|
||||
:timestamp="record.punchTime || record.statusName || '未打卡'"
|
||||
:type="record.punched ? 'primary' : 'info'"
|
||||
:color="record.punched ? '#2b6ce8' : '#c0c4cc'"
|
||||
@@ -2217,10 +2223,10 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="isStandaloneWaybillDetailPage" class="waybill-manage-page__detail-footer">
|
||||
<div v-if="detailPageLocked" class="waybill-manage-page__detail-footer">
|
||||
<el-button type="primary" @click="closeDetail">关闭</el-button>
|
||||
</div>
|
||||
<template v-if="!isStandaloneWaybillDetailPage" #footer>
|
||||
<template v-if="!detailPageLocked" #footer>
|
||||
<el-button type="primary" @click="closeDetail">关闭</el-button>
|
||||
</template>
|
||||
</component>
|
||||
@@ -3512,9 +3518,26 @@ export default {
|
||||
selectionList: [],
|
||||
attachmentUploadMode: false,
|
||||
standaloneFormKey: '',
|
||||
// 实例所属路由的路径快照:用于判断当前 $route 是否还是「本实例自己的路由」。
|
||||
// 必须在 data 里初始化,不能放 created —— Vue 的 Options API 执行顺序是
|
||||
// data → computed → watch(immediate) → created,放 created 会被首个
|
||||
// immediate watcher 读到空值,把本该执行的首次加载也挡掉。
|
||||
routePathLocked: this.$route.path,
|
||||
// 页面形态(列表 / 独立表单页 / 独立详情页)在实例创建时锁定一次,之后不再随 $route 变化。
|
||||
// 原因:标签页 keep-alive 只会让实例 deactivate,实例仍会随 $route(全局响应式)重新渲染;
|
||||
// 若形态跟着路由翻回列表/弹窗态,缓存实例会渲染出 append-to-body 的 el-dialog —— 弹窗被
|
||||
// Teleport 到 body 后,KeepAlive.deactivate 的 move 搬不动它,DOM 会永久残留在页面上,
|
||||
// 表现为“从运单详情/独立表单离开后点其它菜单,详情弹窗又冒出来”。
|
||||
formPageLocked: false,
|
||||
detailPageLocked: false,
|
||||
// 同上:表单模式(add / edit)也一次性锁定,避免缓存实例随 $route 变 query 导致 option 变形
|
||||
formModeLocked: '',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.formPageLocked = this.isStandaloneWaybillFormPage;
|
||||
this.detailPageLocked = this.isStandaloneWaybillDetailPage;
|
||||
this.formModeLocked = this.$route.query.mode || '';
|
||||
if (this.config.enableAllDept && this.isAdmin) {
|
||||
this.allDept = 1;
|
||||
}
|
||||
@@ -3538,6 +3561,14 @@ export default {
|
||||
this.consumeTemplateCreatePayload();
|
||||
}
|
||||
},
|
||||
// 标签切走(实例被 keep-alive 缓存)或销毁时,收起本页所有 append-to-body 的弹窗。
|
||||
// 否则 Teleport 出去的弹窗 DOM 会一直留在 body 上,盖住后续打开的页面。
|
||||
deactivated() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['permission', 'userInfo']),
|
||||
permissionList() {
|
||||
@@ -3545,6 +3576,25 @@ export default {
|
||||
addBtn: this.canCreate,
|
||||
};
|
||||
},
|
||||
// 打卡时间轴按打卡时间先后展示:已打卡记录按打卡时间升序在前,
|
||||
// 未打卡记录保持流程节点原顺序排在其后。
|
||||
orderedWaybillPunchRecords() {
|
||||
const records = Array.isArray(this.waybillPunchRecords) ? this.waybillPunchRecords : [];
|
||||
return records
|
||||
.map((record, index) => ({
|
||||
record,
|
||||
index,
|
||||
punchedAt: this.waybillPunchRecordTimestamp(record),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const aPunched = a.punchedAt !== null;
|
||||
const bPunched = b.punchedAt !== null;
|
||||
if (aPunched && bPunched) return a.punchedAt - b.punchedAt || a.index - b.index;
|
||||
if (aPunched !== bPunched) return aPunched ? -1 : 1;
|
||||
return a.index - b.index;
|
||||
})
|
||||
.map(item => item.record);
|
||||
},
|
||||
isStandaloneWaybillPage() {
|
||||
return this.standaloneFormPage && standaloneWaybillFormPaths.includes(this.$route.path);
|
||||
},
|
||||
@@ -3554,8 +3604,15 @@ export default {
|
||||
isStandaloneWaybillDetailPage() {
|
||||
return this.standaloneDetailPage && this.$route.path === standaloneWaybillDetailRoute;
|
||||
},
|
||||
// 容器形态读实例创建时锁定的标志,不随 $route 翻转(详见 data 中 formPageLocked 的说明)
|
||||
// 当前路由是否仍属于本实例(tab.js 按 fullPath 建实例,一个实例只对应一个 path)。
|
||||
// 被 keep-alive 缓存后如果 $route 已经跑到别的页面上,就不该再用「当前页面的 id」
|
||||
// 去触发本实例的请求或跳转,否则会拿别的单据 id 查自己的接口。
|
||||
isOwnedRouteActive() {
|
||||
return this.$route.path === this.routePathLocked;
|
||||
},
|
||||
detailContainer() {
|
||||
return this.isStandaloneWaybillDetailPage ? 'PageDetail' : 'el-dialog';
|
||||
return this.detailPageLocked ? 'PageDetail' : 'el-dialog';
|
||||
},
|
||||
formPageTitle() {
|
||||
if (this.isStandaloneWaybillFormPage) {
|
||||
@@ -3567,13 +3624,13 @@ export default {
|
||||
return '';
|
||||
},
|
||||
crudContainer() {
|
||||
return this.isStandaloneWaybillFormPage ? 'PageAvueForm' : 'avue-crud';
|
||||
return this.formPageLocked ? 'PageAvueForm' : 'avue-crud';
|
||||
},
|
||||
pageFormOption() {
|
||||
if (!this.isStandaloneWaybillFormPage) return this.option;
|
||||
if (!this.formPageLocked) return this.option;
|
||||
const base = {
|
||||
...this.option,
|
||||
boxType: this.$route.query.mode === 'edit' ? 'edit' : 'add',
|
||||
boxType: this.formModeLocked === 'edit' ? 'edit' : 'add',
|
||||
menuBtn: true,
|
||||
submitBtn: true,
|
||||
emptyBtn: false,
|
||||
@@ -3821,6 +3878,10 @@ export default {
|
||||
detailId: {
|
||||
immediate: true,
|
||||
handler(id) {
|
||||
// 宿主 view 传的是全局 $route.query.id / detailId,实例被缓存后仍会被重渲染,
|
||||
// 必须确认当前路由还是自己的:否则跳到别的详情页时会把「别的单据 id」灌进来,
|
||||
// 触发 openDetail 用错 id 查运单 → 报「运单管理不存在」。
|
||||
if (!this.isOwnedRouteActive) return;
|
||||
if (id !== undefined && id !== null && id !== '') {
|
||||
this.$nextTick(() => this.openDetail({ id }));
|
||||
}
|
||||
@@ -3837,6 +3898,25 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 页面被 keep-alive 缓存/卸载时调用,关闭本页所有 append-to-body 的弹窗,
|
||||
// 避免 Teleport 出去的 DOM 残留在 body 上盖住后续页面(详见 data 里的说明)。
|
||||
closeInnerDialogs() {
|
||||
this.detailBox = false;
|
||||
this.mileageDialog.visible = false;
|
||||
this.attachmentDocumentPreviewVisible = false;
|
||||
this.attachmentImagePreviewVisible = false;
|
||||
this.waybillRouteChangeBox = false;
|
||||
this.waybillCommonAddressBox = false;
|
||||
this.flowBox = false;
|
||||
this.transportRouteBox = false;
|
||||
this.transportAddressBox = false;
|
||||
this.transportStationBox = false;
|
||||
this.transportMapBox = false;
|
||||
this.commonCargoBox = false;
|
||||
this.cargoImportBox = false;
|
||||
this.waybillProcessConfigBox = false;
|
||||
this.excelBox = false;
|
||||
},
|
||||
initStandaloneFormPage() {
|
||||
if (!this.isStandaloneWaybillFormPage) return;
|
||||
const mode = this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
||||
@@ -4384,6 +4464,9 @@ export default {
|
||||
},
|
||||
openDetail(row) {
|
||||
if (!this.isStandaloneWaybillDetailPage) {
|
||||
// 已不在自己的路由上(被缓存后路由切走):此时 row.id 极可能是别的单据的 id,
|
||||
// 再 push 会生成一堆错误的「运单管理详情」标签
|
||||
if (!this.isOwnedRouteActive) return;
|
||||
this.$router.push({ path: standaloneWaybillDetailRoute, query: { id: row.id } });
|
||||
return;
|
||||
}
|
||||
@@ -4463,7 +4546,10 @@ export default {
|
||||
this.$message.info('当前配载单暂无详情数据');
|
||||
return;
|
||||
}
|
||||
this.$router.push({ path: '/business/loading-manage', query: { detailId: id } });
|
||||
this.$router.push({
|
||||
path: '/business/loading-manage/detail',
|
||||
query: { id, name: '配载单详情' },
|
||||
});
|
||||
},
|
||||
loadWaybillDetailProcessNodes(projectId) {
|
||||
this.waybillDetailProcessNodes = [];
|
||||
@@ -4492,6 +4578,19 @@ export default {
|
||||
})
|
||||
.catch(() => []);
|
||||
},
|
||||
// 解析打卡时间(支持常见日期字符串格式),未打卡或无有效时间返回 null
|
||||
waybillPunchRecordTimestamp(record = {}) {
|
||||
if (record.punched === false || record.statusName === '未打卡') return null;
|
||||
const raw = record.punchTime || record.punchAt || record.punchDateTime || '';
|
||||
const text = String(raw).trim();
|
||||
if (!text) return null;
|
||||
const parsed = this.$dayjs ? this.$dayjs(text) : null;
|
||||
if (parsed && typeof parsed.isValid === 'function' && parsed.isValid()) {
|
||||
return parsed.valueOf();
|
||||
}
|
||||
const fallback = new Date(text.replace(/-/g, '/')).getTime();
|
||||
return Number.isNaN(fallback) ? null : fallback;
|
||||
},
|
||||
loadWaybillPunchRecords() {
|
||||
const waybillId = this.detailRow?.id;
|
||||
if (!waybillId || this.waybillPunchRecordsLoading) return;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<basic-container class="loading-manage-page">
|
||||
<div v-if="!isStandaloneFormPage" class="loading-manage-page__search">
|
||||
<div v-if="!isStandalonePage" class="loading-manage-page__search">
|
||||
<el-form :model="searchForm" label-position="right" label-width="88px">
|
||||
<div class="loading-manage-page__search-grid">
|
||||
<el-form-item label="配载单号">
|
||||
@@ -222,7 +222,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="!isStandaloneFormPage" class="loading-manage-page__toolbar">
|
||||
<div v-if="!isStandalonePage" class="loading-manage-page__toolbar">
|
||||
<div class="loading-manage-page__toolbar-left">
|
||||
<el-button type="primary" @click="openLoadingDialog('add')">新建配载</el-button>
|
||||
<el-button type="primary" plain @click="handleExport">批量导出</el-button>
|
||||
@@ -242,7 +242,7 @@
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-if="!isStandaloneFormPage"
|
||||
v-if="!isStandalonePage"
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
@@ -259,7 +259,7 @@
|
||||
<el-link
|
||||
type="primary"
|
||||
class="loading-manage-page__link"
|
||||
@click="openLoadingDialog('view', row)"
|
||||
@click="gotoLoadingDetail(row)"
|
||||
>{{ row.loadingNo }}</el-link
|
||||
>
|
||||
</template>
|
||||
@@ -363,7 +363,7 @@
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div v-if="!isStandaloneFormPage" class="loading-manage-page__pagination">
|
||||
<div v-if="!isStandalonePage" class="loading-manage-page__pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="page.currentPage"
|
||||
v-model:page-size="page.pageSize"
|
||||
@@ -376,27 +376,19 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<component
|
||||
:is="isStandaloneFormPage ? 'div' : 'el-dialog'"
|
||||
:model-value="isStandaloneFormPage ? undefined : dialogVisible"
|
||||
:title="isStandaloneFormPage ? undefined : dialogTitle"
|
||||
:append-to-body="isStandaloneFormPage ? undefined : true"
|
||||
:destroy-on-close="isStandaloneFormPage ? undefined : true"
|
||||
:width="isStandaloneFormPage ? undefined : '96%'"
|
||||
:top="isStandaloneFormPage ? undefined : '3vh'"
|
||||
:class="[
|
||||
'loading-manage-dialog',
|
||||
{ 'loading-manage-form-dialog': isStandaloneFormPage },
|
||||
{ 'loading-manage-form-page': isStandaloneFormPage },
|
||||
]"
|
||||
:modal="isStandaloneFormPage ? undefined : true"
|
||||
:show-close="isStandaloneFormPage ? undefined : true"
|
||||
:close-on-click-modal="isStandaloneFormPage ? undefined : true"
|
||||
:close-on-press-escape="isStandaloneFormPage ? undefined : true"
|
||||
@update:model-value="value => !isStandaloneFormPage && (dialogVisible = value)"
|
||||
@closed="resetLoadingDialog"
|
||||
<!--
|
||||
配载单的新增/编辑/重新派单/详情全部是独立整页,这里固定渲染普通容器,不再按路由切换 el-dialog。
|
||||
原因:标签页 keep-alive 会把本页实例缓存下来,若容器随 $route 由 div 变回 el-dialog,
|
||||
被缓存的实例会在离开本页后渲染出 append-to-body 的弹窗并永久停留在 body 上(Vue 的
|
||||
KeepAlive.deactivate → Teleport.move 不会搬走 append-to-body 的弹窗 DOM),表现为“点左侧菜单又弹出弹窗”。
|
||||
-->
|
||||
<div
|
||||
v-if="isStandalonePage"
|
||||
class="loading-manage-dialog loading-manage-form-dialog loading-manage-form-page"
|
||||
>
|
||||
<div v-if="isStandaloneFormPage" class="archive-page-form__title">{{ formPageTitle }}</div>
|
||||
<div class="archive-page-form__title">{{
|
||||
isStandaloneDetailPage ? detailPageTitle : formPageTitle
|
||||
}}</div>
|
||||
<div v-loading="dialogLoading" class="loading-manage-dialog__body">
|
||||
<div v-if="dialogReadonly" class="loading-detail">
|
||||
<section-card title="配载单详情">
|
||||
@@ -1229,7 +1221,7 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div v-if="isStandaloneFormPage" class="loading-manage-dialog__footer">
|
||||
<div class="loading-manage-dialog__footer">
|
||||
<template v-if="dialogReadonly">
|
||||
<el-button @click="closeLoadingDialog">关闭</el-button>
|
||||
</template>
|
||||
@@ -1243,23 +1235,7 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
<template v-if="!isStandaloneFormPage" #footer>
|
||||
<div class="loading-manage-dialog__footer">
|
||||
<template v-if="dialogReadonly">
|
||||
<el-button @click="closeLoadingDialog">关闭</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button @click="clearLoadingDialog">清空</el-button>
|
||||
<el-button type="primary" plain :loading="dialogSaving === 'draft'" @click="saveDraft">
|
||||
暂存
|
||||
</el-button>
|
||||
<el-button type="primary" :loading="dialogSaving === 'submit'" @click="submitLoading">
|
||||
{{ dialogMode === 'reassign' ? '确认派单' : '确认' }}
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</component>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="routeChangeVisible"
|
||||
title="变更运输路线"
|
||||
@@ -1268,7 +1244,7 @@
|
||||
width="78%"
|
||||
class="loading-route-change-dialog"
|
||||
>
|
||||
<el-tabs v-model="routeChangeTab">
|
||||
<el-tabs type="border-card" v-model="routeChangeTab">
|
||||
<el-tab-pane label="变更路线" name="change">
|
||||
<section-card title="关联子运单地址">
|
||||
<el-table :data="routeChangeRows" border>
|
||||
@@ -1634,7 +1610,6 @@ export default {
|
||||
searchExpanded: false,
|
||||
searchForm: createSearchForm(),
|
||||
selectionRows: [],
|
||||
dialogVisible: false,
|
||||
dialogMode: 'add',
|
||||
dialogLoading: false,
|
||||
dialogSaving: '',
|
||||
@@ -1704,15 +1679,6 @@ export default {
|
||||
if (!this.taskCarrierLoading && !this.taskCarrierOptions.length) return '暂无可用承运商合同';
|
||||
return '请选择';
|
||||
},
|
||||
dialogTitle() {
|
||||
const titleMap = {
|
||||
add: '新建配载',
|
||||
edit: '编辑配载单',
|
||||
view: '配载单详情',
|
||||
reassign: '重新派单',
|
||||
};
|
||||
return titleMap[this.dialogMode] || '配载管理';
|
||||
},
|
||||
detailStatusType() {
|
||||
const typeMap = {
|
||||
pending: 'success',
|
||||
@@ -1741,51 +1707,81 @@ export default {
|
||||
}
|
||||
);
|
||||
},
|
||||
// 独立表单页:/business/loading-manage/form?mode=add|edit|reassign
|
||||
isStandaloneFormPage() {
|
||||
return (
|
||||
this.$route.path === '/business/loading-manage/form' &&
|
||||
['add', 'edit'].includes(this.$route.query.mode)
|
||||
['add', 'edit', 'reassign'].includes(this.$route.query.mode)
|
||||
);
|
||||
},
|
||||
// 独立详情页:/business/loading-manage/detail?id=xxx
|
||||
isStandaloneDetailPage() {
|
||||
return this.$route.path === '/business/loading-manage/detail';
|
||||
},
|
||||
// 列表容器隐藏搜索栏 / 工具栏 / 表格 / 分页的统一开关
|
||||
isStandalonePage() {
|
||||
return this.isStandaloneFormPage || this.isStandaloneDetailPage;
|
||||
},
|
||||
formPageDefaultTitle() {
|
||||
const titleMap = {
|
||||
add: '新增配载管理',
|
||||
edit: '编辑配载管理',
|
||||
reassign: '重新派单',
|
||||
};
|
||||
return titleMap[this.$route.query.mode] || '新增配载管理';
|
||||
},
|
||||
formPageTitle() {
|
||||
return (
|
||||
this.$route.query.name || `${this.$route.query.mode === 'edit' ? '编辑' : '新增'}配载管理`
|
||||
);
|
||||
return this.$route.query.name || this.formPageDefaultTitle;
|
||||
},
|
||||
detailPageTitle() {
|
||||
return this.$route.query.name || '配载单详情';
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadTransportTypeOptions();
|
||||
this.loadTable();
|
||||
if (this.isStandaloneFormPage) {
|
||||
if (this.isStandalonePage) {
|
||||
this.syncStandaloneTagTitle();
|
||||
this.initStandaloneFormPage();
|
||||
if (this.isStandaloneDetailPage) this.initStandaloneDetailPage();
|
||||
else this.initStandaloneFormPage();
|
||||
}
|
||||
// 兼容旧链接:/business/loading-manage?detailId=xxx 直接跳独立详情页
|
||||
const detailId = this.$route.query.detailId;
|
||||
if (detailId) this.openLoadingDialog('view', { id: detailId });
|
||||
if (detailId) this.gotoLoadingDetail({ id: detailId });
|
||||
},
|
||||
// 标签切走(实例被 keep-alive 缓存)或销毁时,先收起页内子弹窗
|
||||
deactivated() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
if (this.isStandaloneFormPage) {
|
||||
this.syncStandaloneTagTitle();
|
||||
if (
|
||||
to.query.mode !== from?.query?.mode ||
|
||||
String(to.query.id || '') !== String(from?.query.id || '') ||
|
||||
String(to.query.copyId || '') !== String(from?.query.copyId || '')
|
||||
) {
|
||||
this.initStandaloneFormPage();
|
||||
}
|
||||
} else if (from?.query?.mode && this.dialogVisible) {
|
||||
this.closeLoadingDialog();
|
||||
if (!this.isStandalonePage) return;
|
||||
this.syncStandaloneTagTitle();
|
||||
if (this.isStandaloneDetailPage) {
|
||||
// 记录变化或上一个页面不是详情态(例如从表单页/列表页进来)时重新拉取详情
|
||||
const idChanged = String(to.query.id || '') !== String(from?.query?.id || '');
|
||||
if (idChanged || this.dialogMode !== 'view') this.initStandaloneDetailPage();
|
||||
return;
|
||||
}
|
||||
if (
|
||||
to.query.mode !== from?.query?.mode ||
|
||||
String(to.query.id || '') !== String(from?.query.id || '') ||
|
||||
String(to.query.copyId || '') !== String(from?.query.copyId || '')
|
||||
) {
|
||||
this.initStandaloneFormPage();
|
||||
}
|
||||
},
|
||||
'$route.query.detailId'(detailId) {
|
||||
if (detailId) this.openLoadingDialog('view', { id: detailId });
|
||||
if (detailId) this.gotoLoadingDetail({ id: detailId });
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
syncStandaloneTagTitle() {
|
||||
if (!this.isStandaloneFormPage) return;
|
||||
const title = this.$route.query.mode === 'edit' ? '编辑配载管理' : '新增配载管理';
|
||||
if (!this.isStandalonePage) return;
|
||||
const title = this.isStandaloneDetailPage ? this.detailPageTitle : this.formPageDefaultTitle;
|
||||
if (this.$route.query.name === title) return;
|
||||
this.$store.commit('SET_TAG', {
|
||||
fullPath: this.$route.fullPath,
|
||||
@@ -2189,9 +2185,9 @@ export default {
|
||||
},
|
||||
handleRowAction(type, row) {
|
||||
const actionMap = {
|
||||
view: () => this.openLoadingDialog('view', row),
|
||||
view: () => this.gotoLoadingDetail(row),
|
||||
edit: () => this.openLoadingDialog('edit', row),
|
||||
reassign: () => this.openLoadingDialog('reassign', row),
|
||||
reassign: () => this.gotoLoadingReassign(row),
|
||||
delete: () => this.deleteLoading(row),
|
||||
start: () => this.startLoading(row),
|
||||
cancel: () => this.cancelLoading(row),
|
||||
@@ -2199,9 +2195,44 @@ export default {
|
||||
};
|
||||
actionMap[type]?.();
|
||||
},
|
||||
// 详情改为独立页面(与新增/编辑一致)
|
||||
gotoLoadingDetail(row = {}) {
|
||||
const id = row.id || row.loadingId || row.loadingManageId;
|
||||
if (!id) {
|
||||
ElMessage.info('当前配载单暂无详情数据');
|
||||
return;
|
||||
}
|
||||
if (this.isStandaloneDetailPage && String(this.$route.query.id || '') === String(id)) return;
|
||||
this.$router.push({
|
||||
path: '/business/loading-manage/detail',
|
||||
query: { id, name: '配载单详情' },
|
||||
});
|
||||
},
|
||||
// 重新派单改为独立页面
|
||||
gotoLoadingReassign(row = {}) {
|
||||
if (!row.id) {
|
||||
ElMessage.warning('配载单不存在,无法重新派单');
|
||||
return;
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/business/loading-manage/form',
|
||||
query: { mode: 'reassign', id: row.id, name: '重新派单' },
|
||||
});
|
||||
},
|
||||
async initStandaloneDetailPage() {
|
||||
if (!this.isStandaloneDetailPage) return;
|
||||
const id = this.$route.query.id;
|
||||
if (!id) {
|
||||
ElMessage.warning('缺少配载单参数,无法查看详情');
|
||||
this.closeLoadingDialog();
|
||||
return;
|
||||
}
|
||||
await this.openLoadingDialog('view', { id });
|
||||
},
|
||||
async initStandaloneFormPage() {
|
||||
if (!this.isStandaloneFormPage) return;
|
||||
const mode = this.$route.query.mode === 'edit' ? 'edit' : 'add';
|
||||
const queryMode = this.$route.query.mode;
|
||||
const mode = ['edit', 'reassign'].includes(queryMode) ? queryMode : 'add';
|
||||
const copyId = mode === 'add' ? this.$route.query.copyId : '';
|
||||
await this.openLoadingDialog(mode, {
|
||||
id: copyId || this.$route.query.id,
|
||||
@@ -2222,7 +2253,12 @@ export default {
|
||||
}
|
||||
},
|
||||
async openLoadingDialog(mode, row) {
|
||||
if (['add', 'edit'].includes(mode) && !this.isStandaloneFormPage) {
|
||||
// 列表页进入新增/编辑/重新派单:统一走独立页面
|
||||
if (['add', 'edit', 'reassign'].includes(mode) && !this.isStandaloneFormPage) {
|
||||
if (mode === 'reassign') {
|
||||
this.gotoLoadingReassign(row || {});
|
||||
return;
|
||||
}
|
||||
this.$router.push({
|
||||
path: '/business/loading-manage/form',
|
||||
query: {
|
||||
@@ -2234,7 +2270,6 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.dialogMode = mode;
|
||||
this.dialogVisible = true;
|
||||
this.dialogLoading = true;
|
||||
this.resetDialogData();
|
||||
if (mode === 'add') this.candidateQuery.transportType = 'road';
|
||||
@@ -2267,17 +2302,9 @@ export default {
|
||||
}
|
||||
},
|
||||
closeLoadingDialog() {
|
||||
if (this.isStandaloneFormPage) {
|
||||
this.$router.$avueRouter?.closeTag?.();
|
||||
this.$router.push({ path: '/business/loading-manage', query: {} });
|
||||
return;
|
||||
}
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
resetLoadingDialog() {
|
||||
this.resetDialogData();
|
||||
this.dialogSaving = '';
|
||||
this.dialogLoading = false;
|
||||
if (!this.isStandalonePage) return;
|
||||
this.$router.$avueRouter?.closeTag?.();
|
||||
this.$router.push({ path: '/business/loading-manage', query: {} });
|
||||
},
|
||||
resetDialogData() {
|
||||
this.detailAmap?.destroy?.();
|
||||
@@ -2299,10 +2326,11 @@ export default {
|
||||
this.processProjectId = '';
|
||||
this.processProjectOptions = [];
|
||||
},
|
||||
clearLoadingDialog() {
|
||||
this.resetDialogData();
|
||||
if (this.dialogMode === 'add') this.candidateQuery.transportType = 'road';
|
||||
this.loadCandidateWaybills();
|
||||
// 离开本页(标签缓存/卸载)时收起页内子弹窗,避免 append-to-body 的弹窗 DOM 残留在 body 上
|
||||
closeInnerDialogs() {
|
||||
this.routeChangeVisible = false;
|
||||
this.commonAddressVisible = false;
|
||||
this.candidateSearchVisible = false;
|
||||
},
|
||||
restoreRouteAndCargo() {
|
||||
const routeRows = parseJsonArray(this.dialogForm.routeJson);
|
||||
@@ -3433,7 +3461,6 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.loading-manage-dialog__table-toolbar {
|
||||
@@ -3767,7 +3794,9 @@ export default {
|
||||
box-sizing: border-box;
|
||||
background: #f5f6fa;
|
||||
}
|
||||
:global(.loading-manage-form-page .loading-manage-dialog__body) {
|
||||
/* 独立页面(新增/编辑/重新派单/详情):内容随 #avue-view 自然滚动,不做内嵌 78vh 滚动盒。
|
||||
此处权重必须高于 .loading-manage-dialog .loading-manage-dialog__body,故带上 .loading-manage-page 外壳。 */
|
||||
:global(.loading-manage-page .loading-manage-form-dialog.loading-manage-form-page .loading-manage-dialog__body) {
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
padding-right: 0;
|
||||
|
||||
@@ -1354,13 +1354,6 @@ export default {
|
||||
.filter(fileType => !uploadedTypes.has(fileType));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route.fullPath'() {
|
||||
if (this.isProjectFormPage) {
|
||||
this.openProjectFormPage();
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadDeptOptions();
|
||||
this.loadCargoTypeOptions();
|
||||
@@ -1370,11 +1363,6 @@ export default {
|
||||
this.openProjectFormPage();
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
if (this.isProjectFormPage && !this.projectBox) {
|
||||
this.openProjectFormPage();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buildTableOption() {
|
||||
return {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<!-- 同 waybill-manage-detail.vue:只在当前路由属于本页时才透传 detailId,
|
||||
避免标签页缓存的实例被其它页面的 detailId 污染后误开详情。 -->
|
||||
<transport-plan-page
|
||||
:api="api"
|
||||
:config="config"
|
||||
:crud-option="option"
|
||||
:detail-id="$route.query.detailId"
|
||||
:detail-id="detailId"
|
||||
:menu-width="220"
|
||||
standalone-form-page
|
||||
/>
|
||||
@@ -14,6 +16,8 @@ import TransportPlanPage from './components/transport-plan-page.vue';
|
||||
import * as api from '@/api/business/transport-plan';
|
||||
import { config, option } from '@/option/business/transport-plan';
|
||||
|
||||
const listRoutePath = '/business/transport-plan';
|
||||
|
||||
export default {
|
||||
components: { TransportPlanPage },
|
||||
data() {
|
||||
@@ -23,5 +27,10 @@ export default {
|
||||
option,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
detailId() {
|
||||
return this.$route.path === listRoutePath ? this.$route.query.detailId : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<!--
|
||||
detailId 必须做路由守卫:本组件对应 /business/waybill-manage/detail,
|
||||
而 $route 是全局响应式的。标签页 keep-alive 会缓存本实例,路由切到别的详情页后
|
||||
实例仍会重渲染,若无守卫就会把「当前页面的 id」灌进子组件,导致它拿别的单据 id
|
||||
去查运单 → 报「运单管理不存在」,并误生成一个又一个运单详情标签。
|
||||
-->
|
||||
<waybill-manage-page
|
||||
:api="api"
|
||||
:config="config"
|
||||
:crud-option="option"
|
||||
:detail-id="$route.query.id"
|
||||
:detail-id="detailId"
|
||||
:menu-width="250"
|
||||
standalone-detail-page
|
||||
/>
|
||||
@@ -14,10 +20,17 @@ import WaybillManagePage from './components/waybill-manage-page.vue';
|
||||
import * as api from '@/api/business/waybill-manage';
|
||||
import { config, option } from '@/option/business/waybill-manage';
|
||||
|
||||
const detailRoutePath = '/business/waybill-manage/detail';
|
||||
|
||||
export default {
|
||||
components: { WaybillManagePage },
|
||||
data() {
|
||||
return { api, config, option };
|
||||
},
|
||||
computed: {
|
||||
detailId() {
|
||||
return this.$route.path === detailRoutePath ? this.$route.query.id : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<!-- 同 waybill-manage-detail.vue:保留旧链接 /business/waybill-manage?detailId=xxx 的能力,
|
||||
但只在当前路由确实属于本页时才透传,避免被其它页面的 detailId 污染。 -->
|
||||
<waybill-manage-page
|
||||
:api="api"
|
||||
:config="config"
|
||||
:crud-option="option"
|
||||
:detail-id="$route.query.detailId"
|
||||
:detail-id="detailId"
|
||||
:menu-width="250"
|
||||
standalone-form-page
|
||||
/>
|
||||
@@ -14,6 +16,8 @@ import WaybillManagePage from './components/waybill-manage-page.vue';
|
||||
import * as api from '@/api/business/waybill-manage';
|
||||
import { config, option } from '@/option/business/waybill-manage';
|
||||
|
||||
const listRoutePath = '/business/waybill-manage';
|
||||
|
||||
export default {
|
||||
components: { WaybillManagePage },
|
||||
data() {
|
||||
@@ -23,5 +27,10 @@ export default {
|
||||
option,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
detailId() {
|
||||
return this.$route.path === listRoutePath ? this.$route.query.detailId : '';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user