解决 Element-UI 中 el-dialog 与 MessageBox 遮罩层冲突问题

558 阅读2分钟

问题的出现

在项目中,由于特定的业务场景要求,需要将 el-dialog 的遮罩层限制在父容器内显示,而不是默认的添加到 body 元素下。通过查阅 Element-UI 的文档,发现可以给 el-dialog 设置 :modal-append-to-body='false' 来实现这一需求。设置后,遮罩层成功地显示在了父容器内,看似完美地解决了需求。

然而,新的问题出现了,当在 el-dialog 调用MessageBox组件(如二次确认框),关闭MessageBox时遮罩层依然残留。

原因分析

经过调试和分析,发现导致这个问题的根源在于 DialogMessageBox 在 Element-UI 的设计中,在全局只维护一个遮罩层 DOM 节点。

当我们设置 :modal-append-to-body='false' 时,改变了 el-dialog 遮罩层的默认挂载位置,但这并没有改变全局遮罩层的管理机制。此时,在 el-dialog 内部使用 MessageBoxMessageBox 依然尝试操作全局唯一的那个遮罩层节点,由于遮罩层挂载位置和相关逻辑的改变,就导致了遮罩层无法正常关闭的情况。

从源码层面来看,Element-UI 在创建和管理遮罩层时,通过一些特定的方法和逻辑来控制遮罩层的显示、隐藏以及相关交互。当我们修改了 el-dialog 的遮罩层挂载方式,打破了原有的平衡,使得 MessageBox 对遮罩层的操作失效。

解决方案

为了解决这个问题,我们需要重新设计遮罩层的实现方式,不再依赖 Element-UI 默认的全局遮罩层管理。具体的解决方案如下:

1. 手动添加自定义遮罩层

在父容器下手动添加一个遮罩层元素,通过 v-if 指令来控制其显示与隐藏,代码如下:

<div v-if="dialogVisible" class="custom-mask" />

这里通过 dialogVisible 变量来控制遮罩层的显示状态,当 dialogVisibletrue ,即 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>