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:
@@ -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);
|
||||
|
||||
@@ -140,14 +140,15 @@
|
||||
@load="onLoad(page, query)"
|
||||
/>
|
||||
|
||||
<component
|
||||
:is="projectFormContainer"
|
||||
v-if="projectBox"
|
||||
v-bind="projectFormContainerProps"
|
||||
@update:model-value="projectBox = $event"
|
||||
@closed="resetProjectDialog"
|
||||
>
|
||||
<div v-if="isProjectFormPage" class="archive-page-form__title">
|
||||
<!--
|
||||
项目新增/编辑/查看/变更/补录全部是独立整页(openProjectDialog 一律 push /business/project-apply/form),
|
||||
这里固定渲染普通容器,不再按 $route 在 div 与 el-dialog 之间切换。
|
||||
原因:标签页 keep-alive 会缓存本页实例,若容器在离开本页后又变回 el-dialog,
|
||||
缓存实例会把 append-to-body 的弹窗渲染进 body 并永久停留(Vue 的 KeepAlive.deactivate →
|
||||
Teleport.move 不会搬走 append-to-body 的弹窗 DOM),表现为“点左侧菜单又弹出新增项目管理弹窗”。
|
||||
-->
|
||||
<div v-if="isProjectFormPage" class="project-apply-page-form">
|
||||
<div class="archive-page-form__title">
|
||||
{{ projectDialogTitle }}
|
||||
</div>
|
||||
<el-form
|
||||
@@ -788,8 +789,7 @@
|
||||
:class="{ 'project-apply-dialog__footer--change': isChangeDialog }"
|
||||
>
|
||||
<template v-if="isChangeDialog">
|
||||
<el-button v-if="!isProjectFormPage" @click="handleCancelProject">取消</el-button>
|
||||
<el-button v-if="isProjectFormPage" @click="closeProjectForm">取消</el-button>
|
||||
<el-button @click="closeProjectForm">取消</el-button>
|
||||
<el-button type="primary" plain :loading="submitLoading" @click="saveChangeProject">
|
||||
保存
|
||||
</el-button>
|
||||
@@ -798,8 +798,7 @@
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button v-if="!isProjectFormPage" @click="handleCancelProject">取消</el-button>
|
||||
<el-button v-if="isProjectFormPage" @click="closeProjectForm">取消</el-button>
|
||||
<el-button @click="closeProjectForm">取消</el-button>
|
||||
<el-button
|
||||
v-if="!dialogReadonly"
|
||||
type="primary"
|
||||
@@ -819,7 +818,7 @@
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</component>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="attachmentDocumentPreviewVisible"
|
||||
@@ -1116,7 +1115,6 @@ export default {
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
projectBox: false,
|
||||
dialogType: 'add',
|
||||
dialogReadonly: false,
|
||||
submitLoading: false,
|
||||
@@ -1310,21 +1308,6 @@ export default {
|
||||
isProjectFormPage() {
|
||||
return this.$route.path === '/business/project-apply/form';
|
||||
},
|
||||
projectFormContainer() {
|
||||
return this.isProjectFormPage ? 'div' : 'el-dialog';
|
||||
},
|
||||
projectFormContainerProps() {
|
||||
if (this.isProjectFormPage) return { class: 'project-apply-page-form' };
|
||||
return {
|
||||
modelValue: this.projectBox,
|
||||
title: this.projectDialogTitle,
|
||||
appendToBody: true,
|
||||
destroyOnClose: true,
|
||||
width: '1440px',
|
||||
top: '4vh',
|
||||
class: 'project-apply-dialog',
|
||||
};
|
||||
},
|
||||
isBasicInfoReadonly() {
|
||||
return this.dialogReadonly || this.isChangeDialog;
|
||||
},
|
||||
@@ -1363,6 +1346,12 @@ export default {
|
||||
this.openProjectFormPage();
|
||||
}
|
||||
},
|
||||
deactivated() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.closeInnerDialogs();
|
||||
},
|
||||
methods: {
|
||||
buildTableOption() {
|
||||
return {
|
||||
@@ -1390,7 +1379,6 @@ export default {
|
||||
const id = this.$route.query.id;
|
||||
this.dialogType = type;
|
||||
this.dialogReadonly = type === 'view';
|
||||
this.projectBox = true;
|
||||
if (['add', 'majorSupplement'].includes(type)) {
|
||||
this.applyProjectDetail({
|
||||
...emptyForm(),
|
||||
@@ -1411,11 +1399,15 @@ export default {
|
||||
});
|
||||
},
|
||||
closeProjectForm() {
|
||||
if (this.isProjectFormPage) {
|
||||
this.$router.push('/business/project-apply');
|
||||
} else {
|
||||
this.projectBox = false;
|
||||
}
|
||||
this.$router.push('/business/project-apply');
|
||||
},
|
||||
// 页面被 keep-alive 缓存/卸载时,关闭本页所有 append-to-body 的二级弹窗。
|
||||
// 若不关闭,Teleport 出去的弹窗 DOM 在实例失活时不会被搬离 body,会残留盖在后续页面上。
|
||||
closeInnerDialogs() {
|
||||
this.changeRecordDetailVisible = false;
|
||||
this.attachmentDocumentPreviewVisible = false;
|
||||
this.attachmentImagePreviewVisible = false;
|
||||
this.userBox = false;
|
||||
},
|
||||
statusValue(row) {
|
||||
return row[this.config.statusProp || 'status'];
|
||||
@@ -1634,17 +1626,6 @@ export default {
|
||||
else if (type === 'change') query.name = '项目变更';
|
||||
this.$router.push({ path: '/business/project-apply/form', query });
|
||||
},
|
||||
resetProjectDialog() {
|
||||
this.$refs.projectForm?.clearValidate();
|
||||
this.form = emptyForm();
|
||||
this.customerRows = [];
|
||||
this.carrierRows = [];
|
||||
this.attachmentRows = [];
|
||||
this.selectedAttachmentRows = [];
|
||||
this.changeRows = [];
|
||||
this.selectedCustomerId = [];
|
||||
this.selectedCarrierIds = [];
|
||||
},
|
||||
applyProjectDetail(row) {
|
||||
const displayRow = {
|
||||
...row,
|
||||
@@ -1767,23 +1748,6 @@ export default {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
},
|
||||
handleCancelProject() {
|
||||
if (this.isProjectFormPage) {
|
||||
this.closeProjectForm();
|
||||
return;
|
||||
}
|
||||
if (!['add', 'majorSupplement', 'change'].includes(this.dialogType)) {
|
||||
this.closeProjectForm();
|
||||
return;
|
||||
}
|
||||
this.$confirm('确认后直接关闭弹窗,列表无数据变更,是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
this.closeProjectForm();
|
||||
});
|
||||
},
|
||||
saveChangeProject() {
|
||||
this.submitChangeForm(false);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user