一、封装原因

因为我在开发中不想使用 el-dialog 直接写页面,我想向下面这样使用组件,这样包括弹框头部、内容、底部都可以以组件形式传入插件实现分离控制

DialogBox({
   title: item.name == 'qq' ? 'QQ名片' : '微信名片',
   dom: QqCode,
   option: {
     type: item.name
   }
})

二、具体代码如下

主内容文件----index.vue
<template>
  <el-dialog
    class="dialog-plus"
    modal-class="dialog-plus-modal"
    :modal="props.modal"
    :close-on-click-modal="props.closeOnClickModal"
    align-center
    v-model="visible"
    :width="props.width"
    :show-close="false"
    @close="dealClose"
    @closed="dealClosed"
  >
    <div class="dialog-content">
      <component
        :is="props.dom"
        v-bind="props.option"
        v-on="{
          ...props.events,
          close: closeDialog
        }"
      />
    </div>
    <template #header>
      <div class="dialog-header">
        <div class="dialog-title">
          <component
            v-if="headerDom"
            :is="props.headerDom"
            v-bind="props.headerOption"
            v-on="{
              ...props.headerEvents,
              close: closeDialog
            }"
          />
          <template v-else>{{ props.title }}</template>
        </div>
        <i-ep-close class="dialog-close" @click="closeDialog" />
      </div>
    </template>
    <template #footer v-if="showFooter">
      <component
        v-if="footerDom"
        :is="props.footerDom"
        v-bind="props.footerOption"
        v-on="{
          ...props.footerEvents,
          close: closeDialog
        }"
      />
      <div v-else class="dialog-footer">
        <el-button @click="closeDialog">取消</el-button>
        <el-button type="primary" @click="dealConfirm"> 确认 </el-button>
      </div>
    </template>
  </el-dialog>
</template>

<script setup>
defineOptions({
  name: 'DialogPlus'
})
const props = defineProps({
  title: {
    type: String,
    default: '标题'
  },
  width: {
    type: [Number, String],
    default: 500
  },
  modal: {
    type: Boolean,
    default: true
  },
  closeOnClickModal: {
    type: Boolean,
    default: true
  },
  dom: {
    type: Object,
    default: null
  },
  option: {
    type: Object,
    default: () => ({})
  },
  events: {
    type: Object,
    default: () => ({})
  },
  headerDom: {
    type: Object,
    default: null
  },
  headerOption: {
    type: Object,
    default: () => ({})
  },
  headerEvents: {
    type: Object,
    default: () => ({})
  },
  showFooter: {
    type: Boolean,
    default: false
  },
  footerDom: {
    type: Object,
    default: null
  },
  footerOption: {
    type: Object,
    default: () => ({})
  },
  footerEvents: {
    type: Object,
    default: () => ({})
  },
  immediately: {
    type: Boolean,
    default: true
  },
  close: {
    type: Function,
    default: null
  },
  closed: {
    type: Function,
    default: null
  },
  confirm: {
    type: Function,
    default: null
  }
})
const visible = ref(false)

onMounted(() => {
  visible.value = true
})

const closeDialog = () => {
  visible.value = false
}

const dealClose = () => {
  props.close && props.close()
}

const dealClosed = () => {
  props.closed && props.closed()
}

const dealConfirm = () => {
  if (!props.confirm) return
  if (props.immediately) {
    visible.value = false
    props.confirm()
  } else {
    props.confirm(() => {
      visible.value = false
    })
  }
}
</script>

<style lang="scss">
.dialog-plus {
  margin: auto !important;
  min-height: 500px;
  height: fit-content;
  background: #03151fca;
  border: 1px solid #2aa8ff;
  box-shadow: 0px 1px 8px 0px #2aa8ff, inset 0px 1px 5px 0px #2aa8ff;
  color: #fff;
  padding: 10px;
  .el-dialog__header {
    position: relative;
    box-sizing: border-box;
    padding: 0 10px;
    line-height: 50px;
    height: 50px;
    width: 100%;
    background: linear-gradient(
      45deg,
      rgb(2 103 153 / 82%) 0%,
      rgb(0 55 71 / 56%) 100%
    );
    border-radius: 2px;
    .dialog-header {
      width: 100%;
      height: 100%;
      position: relative;
      display: flex;
      flex-flow: row nowrap;
      justify-content: space-between;
      align-items: center;
      .dialog-title {
        font-size: 18px;
        text-align: left;
      }
      .dialog-close {
        font-size: 18px;
        cursor: pointer;
        transition: all 0.2s;
        &:hover {
          color: rgb(40, 205, 255);
        }
      }
    }
    .el-dialog__headerbtn {
      top: 50%;
      z-index: 10;
      transform: translateY(-50%);
      .el-dialog__close {
        color: #fff;
        font-size: 25px;
        transition: all 0.2s;
      }
      &:hover {
        .el-dialog__close {
          color: rgb(40, 205, 255);
        }
      }
    }
  }
  .el-dialog__body {
    height: 100%;
    padding: 0;
    color: #fff;
    .dialog-content {
      padding: 10px 0;
    }
  }
  .dialog-footer {
    display: flex;
    flex-flow: row nowrap;
    justify-content: center;
    align-items: center;
  }
}
</style>
实现函数式封装的方法文件 index.js
import { createApp } from 'vue'
import DialogPlus from './index.vue'
import { uniqueId } from 'lodash-es'

let instance = null
let instanceApp = null
let isExist = null
let isExistApp = null

const DialogBox = (data) => {
  if (!data.dom) {
    console.log('内容缺失')
    return false
  }
  if (data.coexist) {
    isExist = document.createElement('div')
    isExist.setAttribute('id', uniqueId('dialog-'))
    document.body.appendChild(isExist)
    isExistApp = createApp(DialogPlus, {
      ...data,
      closed: () => {
        isExistApp.unmount()
        isExist.remove()
        data.closed && data.closed()
      }
    })
    isExistApp.mount(isExist)
  } else {
    if (isExist) {
      isExistApp.unmount()
      isExist.remove()
    }
    if (instance) {
      instanceApp.unmount()
      instance.remove()
    } // 移除已有弹窗,确保只有一个弹窗显示
    instance = document.createElement('div')
    instance.setAttribute('id', uniqueId('dialog-'))
    document.body.appendChild(instance)
    instanceApp = createApp(DialogPlus, {
      ...data,
      closed: () => {
        instanceApp.unmount()
        instance.remove()
        data.closed && data.closed()
      }
    })
    instanceApp.mount(instance)
  }
}
export default DialogBox
使用方法
DialogBox({
    title: '123',
    dom: QqCode,
    option: {}
})

三、参数说明

参数 说明 备注

title

标题
 width 宽度
 modal 是否要蒙层

closeOnClickModal

点击蒙层关闭弹窗

dom

内容组件(VUE 组件)

option

传给内容组件的参数

events

绑定到内容组件的事件

headerDom

头部组件(VUE 组件) 不传默认显示 title

headerOption

传给头部组件的参数

headerEvents

绑定到头部组件的事件

showFooter

是否有底部

footerDom

底部组件(VUE 组件) 不传默认显示默认的底部组件

footerOption

传给底部组件的参数

footerEvents

绑定到底部组件的事件

immediately

默认底部组件点击确认按钮是否立即关闭弹窗

close

关闭弹窗参数

closed

彻底关闭弹窗参数

confirm

确认关闭弹窗参数

效果如下图

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐