vue3编程 -动态多开模态框实现方案

页面按需弹出多个模态框:

一、v-for方案:

采用v-for提前生成多个模态框实例,采用这种方案的案例社区已经很多

二、采用h函数方案:

代码如下,代码可运行:

基本思路:

  1. 封装模态框组件DialogModal

2.通过h()函数创建DialogModal的虚拟dom

  1. 通过创建容器元素+vue的mount()函数进行挂载

  2. 模态框关闭后需要解除挂载,通过vue的unmoun()函数

如果模态框是自行开发的,到此应该就可以结束了。但是,基于element plus的模态框,会存在当前模态框阻挡后面模态框点击的问题,解决方案如下:

css 复制代码
.modal-class {
  pointer-events: none;
}
.el-overlay-dialog {
  pointer-events: none;
}
.el-dialog {
  pointer-events: auto;
}

完整代码如下:

javascript 复制代码
<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>
javascript 复制代码
<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>
相关推荐
百万蹄蹄向前冲2 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙2 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan3 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
kyriewen4 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理4 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
+VX:Fegn08955 小时前
计算机毕业设计|基于springboot + vue旅游管理系统(源码+数据库+文档)
java·开发语言·vue.js·spring boot·课程设计
雪芽蓝域zzs5 小时前
第 7 章:双 Y 轴组合图(柱状 + 折线,看板高频场景)
vue.js·echars
IT_陈寒5 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
一 乐5 小时前
二手交易平台|基于springboot + vue二手交易平台(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序
Blanche15005 小时前
利用 RAG 为答疑机器人扩展知识范围
前端