页面按需弹出多个模态框:
一、v-for方案:
采用v-for提前生成多个模态框实例,采用这种方案的案例社区已经很多
二、采用h函数方案:
代码如下,代码可运行:
基本思路:
1. 封装模态框组件DialogModal
2.通过h()函数创建DialogModal的虚拟dom
3. 通过创建容器元素+vue的mount()函数进行挂载
4. 模态框关闭后需要解除挂载,通过vue的unmoun()函数
如果模态框是自行开发的,到此应该就可以结束了。但是,基于element plus的模态框,会存在当前模态框阻挡后面模态框点击的问题,解决方案如下:
.modal-class {
pointer-events: none;
}
.el-overlay-dialog {
pointer-events: none;
}
.el-dialog {
pointer-events: auto;
}
完整代码如下:
<script setup lang="ts">
import DialogModal from '@/components/DialogModal.vue'
import { ref, onUnmounted, h, createApp } from 'vue'
import { ElMessage } from 'element-plus'
const modals = ref([])
const showModal = () => {
let num = Math.random()
createModal({ title: 'test' + num, showMsg: 'hello' + num })
}
const createModal = config => {
const modalRef = ref()
const modalInstance = h(DialogModal, {
ref: modalRef,
title: config.title,
content: config.showMsg,
onClosed: () => {
removeModal(modalRef.value)
},
onConfirmed: () => {
removeModal(modalRef.value)
ElMessage.success('模态框已关闭')
}
})
console.log('modalInstance', modalInstance)
modals.value.push(modalRef)
// 创建虚拟 DOM 并挂载到文档
const container = document.createElement('div')
container.id = 'modal-container' + Math.random()
document.body.appendChild(container)
const app = createApp(modalInstance)
app.mount(container)
return {
unmount: () => {
app.unmount()
document.body.removeChild(container)
}
}
}
const removeModal = modal => {
if (modal) {
modal.unmount()
const index = modals.value.findIndex(m => m.component === modal)
if (index !== -1) {
modals.value.splice(index, 1)
}
}
}
onUnmounted(() => {
modals.value.forEach(modal => {
modal.component.unmount()
})
})
</script>
<template>
<div>
<el-button @click="showModal">显示模态框</el-button>
</div>
<router-view></router-view>
</template>
<template>
<div :class="{ scrollDialog: scrollVisible }">
<el-dialog
v-model="dialogVisible"
:width="props.width"
:destroy-on-close="true"
:close-on-click-modal="false"
:modal="false"
draggable
:z-index="-1"
:fullscreen="false"
@close="closeDialog"
:modal-class="'modal-class'"
>
<template v-slot:header>
<span class="cus-title">{{ props.title }}</span>
</template>
<slot>{{ props.showMsg }}</slot>
<template v-slot:footer>
<span v-if="props.dialogFoot || props.dialogFix" class="dialog-footer">
<el-button v-if="props.dialogFoot" @click="closeDialog">{{ cancleName }}</el-button>
<el-button v-if="props.dialogFix" type="primary" @click="determine">{{ determineName }}</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, defineExpose, defineEmits } from 'vue'
const emits = defineEmits(['determine', 'closeDialog'])
const props = defineProps({
width: {
// 弹框宽度
type: String,
default: '480px'
},
title: {
// 弹框模块名称
type: String,
default: () => {
return ''
}
},
showMsg: {
// 展示的信息
type: String,
default: ''
},
dialogFoot: {
// 显示取消代码
type: Boolean,
default: true
},
dialogFix: {
// 显示确定按钮
type: Boolean,
default: true
},
determineName: {
// 确定键名称
type: String,
default: '确认'
},
cancleName: {
// 取消键名称
type: String,
default: '取消'
}
})
// 弹框显示状态
let dialogVisible = ref(true)
// 控制滚动条样式,超过10条显示滚动条
let scrollVisible = ref(false)
/**
* 打开模态框
*/
function openDialog() {
if (dialogVisible.value) {
return
}
dialogVisible.value = true
}
/**
* 关闭模态框
*/
function closeDialog() {
if (!dialogVisible.value) {
return
}
dialogVisible.value = false
emits('closeDialog') // 用户自定义取消后函数,例如:清空表单数据
}
/**
* @description: 确定事件
* @param null
* @return: null
*/
function determine() {
emits('determine') // 触发组件上的determine事件,用户自定义提交
dialogVisible.value = false
}
/**
* 对外提供打开和关闭模态框,控制模态框和滚动条的显隐
*/
defineExpose({ closeDialog, openDialog, dialogVisible, scrollVisible })
</script>
<style scoped lang="scss">
// @import '@/assets/scss/auditPublic.scss';
.el-dialog__body {
padding: 20px;
}
.el-dialog__footer {
border-top: 1px solid #e9ebef;
}
.cus-title {
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
font-size: 16px;
color: #031129;
display: inline-block;
}
:deep(.el-transfer .el-transfer-panel__item.el-checkbox) {
height: auto !important;
}
:deep(.el-dialog__body) {
max-height: 400px !important;
}
.scrollDialog {
:deep(.el-dialog__body) {
overflow-y: auto !important;
height: 400px !important;
}
}
</style>
<style>
.modal-class {
pointer-events: none;
}
.el-overlay-dialog {
pointer-events: none;
}
.el-dialog {
pointer-events: auto;
}
</style>