问题的出现
在项目中,由于特定的业务场景要求,需要将 el-dialog 的遮罩层限制在父容器内显示,而不是默认的添加到 body 元素下。通过查阅 Element-UI 的文档,发现可以给 el-dialog 设置 :modal-append-to-body='false' 来实现这一需求。设置后,遮罩层成功地显示在了父容器内,看似完美地解决了需求。
然而,新的问题出现了,当在 el-dialog 调用MessageBox组件(如二次确认框),关闭MessageBox时遮罩层依然残留。
原因分析
经过调试和分析,发现导致这个问题的根源在于 Dialog 和 MessageBox 在 Element-UI 的设计中,在全局只维护一个遮罩层 DOM 节点。
当我们设置 :modal-append-to-body='false' 时,改变了 el-dialog 遮罩层的默认挂载位置,但这并没有改变全局遮罩层的管理机制。此时,在 el-dialog 内部使用 MessageBox ,MessageBox 依然尝试操作全局唯一的那个遮罩层节点,由于遮罩层挂载位置和相关逻辑的改变,就导致了遮罩层无法正常关闭的情况。
从源码层面来看,Element-UI 在创建和管理遮罩层时,通过一些特定的方法和逻辑来控制遮罩层的显示、隐藏以及相关交互。当我们修改了 el-dialog 的遮罩层挂载方式,打破了原有的平衡,使得 MessageBox 对遮罩层的操作失效。
解决方案
为了解决这个问题,我们需要重新设计遮罩层的实现方式,不再依赖 Element-UI 默认的全局遮罩层管理。具体的解决方案如下:
1. 手动添加自定义遮罩层
在父容器下手动添加一个遮罩层元素,通过 v-if 指令来控制其显示与隐藏,代码如下:
<div v-if="dialogVisible" class="custom-mask" />
这里通过 dialogVisible 变量来控制遮罩层的显示状态,当 dialogVisible 为 true ,即 el-dialog 显示时,遮罩层也会显示。
2. 修改 el-dialog 属性
在 el-dialog 中,删掉 modal-append-to-body 属性,同时添加 :modal="false" 属性。modal 属性设置为 false 后,el-dialog 将不再使用默认的遮罩层,而是由我们自定义的遮罩层来实现遮罩效果。修改后的 el-dialog 代码如下:
<el-dialog :modal="false" title="对话框" :visible.sync="dialogVisible">
<p>这是对话框的内容\</p>
<el-button @click="dialogVisible = false">关闭\</el-button>
</el-dialog>
3. 完整的 Vue 代码示例
结合上述两点,完整的 Vue 组件代码如下:
<template>
<div class="content" :style="{ transform: dialogVisible? 'scale(1)' : 'none' }">
<div v-if="dialogVisible" class="custom-mask" />
<el-button @click="dialogVisible = true">打开对话框</el-button>
<el-dialog :modal="false" title="对话框" :visible.sync="dialogVisible">
<p>这是对话框的内容</p>
<el-button @click="dialogVisible = false">关闭</el-button>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
dialogVisible: false
};
}
}
</script>
<style scoped>
.content {
height: 500px;
}
/* 遮罩层样式 */
.custom-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
justify-content: center;
align-items: center;
}
</style>