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

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

 一、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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值