fix(views): 修复独立页切换时弹窗残留问题

- 独立整页固定使用普通 div 容器,取消动态切换 el-dialog 避免切换时弹窗 DOM 残留
- 删除相关 dialogVisible、dialogTitle、页脚弹窗动态分支代码以简化逻辑
- watch $route 调整,优化独立页初始化流程,避免不必要的关闭弹窗操作
- 新增 closeInnerDialogs 方法,页面卸载或 keep-alive deactivate 时关闭所有二级弹窗
- 对 loading-manage 与 project-apply 两个模块同步改造,统一处理弹窗状态和容器渲染
- 解决 keep-alive 缓存导致旧弹窗一直挂载在 body 上的问题,改善用户切换体验
This commit is contained in:
2026-09-18 10:17:48 +08:00
parent 5bb072223f
commit b4e12a671f
4 changed files with 119 additions and 170 deletions
+41 -81
View File
@@ -376,27 +376,17 @@
/>
</div>
<component
:is="isStandalonePage ? 'div' : 'el-dialog'"
:model-value="isStandalonePage ? undefined : dialogVisible"
:title="isStandalonePage ? undefined : dialogTitle"
:append-to-body="isStandalonePage ? undefined : true"
:destroy-on-close="isStandalonePage ? undefined : true"
:width="isStandalonePage ? undefined : '96%'"
:top="isStandalonePage ? undefined : '3vh'"
:class="[
'loading-manage-dialog',
{ 'loading-manage-form-dialog': isStandalonePage },
{ 'loading-manage-form-page': isStandalonePage },
]"
:modal="isStandalonePage ? undefined : true"
:show-close="isStandalonePage ? undefined : true"
:close-on-click-modal="isStandalonePage ? undefined : true"
:close-on-press-escape="isStandalonePage ? undefined : true"
@update:model-value="value => !isStandalonePage && (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="isStandalonePage" class="archive-page-form__title">{{
<div class="archive-page-form__title">{{
isStandaloneDetailPage ? detailPageTitle : formPageTitle
}}</div>
<div v-loading="dialogLoading" class="loading-manage-dialog__body">
@@ -1231,7 +1221,7 @@
</el-form>
</div>
<div v-if="isStandalonePage" class="loading-manage-dialog__footer">
<div class="loading-manage-dialog__footer">
<template v-if="dialogReadonly">
<el-button @click="closeLoadingDialog">关闭</el-button>
</template>
@@ -1245,23 +1235,7 @@
</el-button>
</template>
</div>
<template v-if="!isStandalonePage" #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="变更运输路线"
@@ -1636,7 +1610,6 @@ export default {
searchExpanded: false,
searchForm: createSearchForm(),
selectionRows: [],
dialogVisible: false,
dialogMode: 'add',
dialogLoading: false,
dialogSaving: '',
@@ -1706,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',
@@ -1785,25 +1749,29 @@ export default {
const detailId = this.$route.query.detailId;
if (detailId) this.gotoLoadingDetail({ id: detailId });
},
// 标签切走(实例被 keep-alive 缓存)或销毁时,先收起页内子弹窗
deactivated() {
this.closeInnerDialogs();
},
beforeUnmount() {
this.closeInnerDialogs();
},
watch: {
$route(to, from) {
if (this.isStandalonePage) {
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();
}
} 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) {
@@ -2302,7 +2270,6 @@ export default {
return;
}
this.dialogMode = mode;
this.dialogVisible = true;
this.dialogLoading = true;
this.resetDialogData();
if (mode === 'add') this.candidateQuery.transportType = 'road';
@@ -2335,17 +2302,9 @@ export default {
}
},
closeLoadingDialog() {
if (this.isStandalonePage) {
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?.();
@@ -2367,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);