fix(router): 修复运单管理详情路由及合同详情路由重复定义问题
- 为 waybill-manage-page、transport-plan-page 等组件增加 routePathLocked 变量锁定路由路径 - 添加 isOwnedRouteActive 计算属性,防止缓存实例错误读取其它路由的 detailId 导致误请求 - watcher 和 openDetail 方法中添加路由守卫,避免多余的请求和误开详情标签 - 修改宿主视图绑定 detailId,避免直接绑定全局 $route.query 导致缓存污染 - 删除 router/views/index.js 中合同管理详情路由重复定义,防止内容区空白问题 - 优化 waybill-import-dialog 组件样式和结构,增加 section-card 包裹表单区域 - 调整相关组件 data、computed、watch 使用,确保第一次立即 watcher 正确读取路由数据 - 验证所有改动后相关页面 HTTP 200,标签页行为正常,解决「运单管理不存在」和合同详情空白问题
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>
|
||||
|
||||
@@ -3518,6 +3518,11 @@ 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 —— 弹窗被
|
||||
@@ -3600,6 +3605,12 @@ export default {
|
||||
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.detailPageLocked ? 'PageDetail' : 'el-dialog';
|
||||
},
|
||||
@@ -3867,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 }));
|
||||
}
|
||||
@@ -4449,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;
|
||||
}
|
||||
|
||||
@@ -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