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>
相关推荐
慢慢雨夜23 分钟前
uniapp 苹果安全域适配
java·前端·uni-app
凄凄迷人26 分钟前
前端基于Rust实现的Wasm进行图片压缩的技术文档
前端·rust·wasm·图片压缩
敲代码不忘补水28 分钟前
二十种编程语言庆祝中秋节
java·javascript·python·golang·html
我码玄黄35 分钟前
JS 的行为设计模式:策略、观察者与命令模式
javascript·设计模式·命令模式
史努比的大头40 分钟前
前端开发深入了解性能优化
前端
码农研究僧42 分钟前
Java或者前端 实现中文排序(调API的Demo)
java·前端·localecompare·中文排序·collator
营赢盈英1 小时前
OpenAI API key not working in my React App
javascript·ai·openai·reactjs·chatbot
吕永强1 小时前
HTML表单标签
前端·html·表单标签
范特西是只猫2 小时前
echarts map地图动态下钻,自定义标注,自定义tooltip弹窗【完整demo版本】
前端·javascript·echarts