VUE3 封装 element-plus中的 dialog 组件

一、封装原因

因为我在开发中不想使用 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

确认关闭弹窗参数

效果如下图

### Vue3 中使用 Element Plus 对话框组件Vue3Element Plus 组件库中,`ElDialog` 是用于创建对话框的一个重要组件。为了实现 `ElDialog` 的显示与隐藏功能,通常会利用 props 来传递属性并借助自定义事件来更新父级的状态。 #### 基本用法 下面是一个简单的例子展示如何声明和控制一个基本的 ElDialog: ```html <template> <el-button @click="dialogVisible = true">点击打开 Dialog</el-button> <!-- 使用 v-model 实现双向数据绑定 --> <el-dialog v-model="dialogVisible" title="提示"> <p>这是一段信息。</p> </el-dialog> </template> <script setup> import { ref } from &#39;vue&#39; // 定义响应式的布尔变量用来控制弹窗显隐 const dialogVisible = ref(false) </script> ``` 这段代码展示了怎样通过设置 `v-model` 属性到本地状态变量上从而达到自动管理可见性的效果[^1]。 #### 封装成可复用组件 如果希望进一步提高代码重用性和模块化程度,则可以考虑将上述逻辑封装进一个新的子组件内,并提供必要的接口供外部调用者操作该模态窗口的行为。 假设我们有一个名为 `CustomDialog.vue` 的文件如下所示: ```html <!-- CustomDialog.vue --> <template> <el-dialog :visible.sync="innerVisible" @close="$emit(&#39;update:visible&#39;, false)"> <slot></slot> </el-dialog> </template> <script> export default { name: "CustomDialog", props: ["visible"], computed: { innerVisible: { get() { return this.visible; }, set(value) { this.$emit("update:visible", value); } } } }; </script> ``` 在这个定制化的版本里,不仅实现了基于 prop 的单向数据流模式下的父子通信机制(`props -> emit`),同时也支持了更简洁直观的语法糖形式即 `v-bind:v-model` 或者直接简写的 `v-model` 方式来进行视图层面上的数据同步处理。 #### 高级特性 除了基础的功能外,Element Plus 还提供了许多其他有用的配置项以及插槽(slot),允许开发者根据实际需求灵活调整样式布局或是添加额外的内容区域等高级选项。比如可以通过指定不同的 slot 名字来自由安排头部(header), 主体(body) 及底部(footer) 的具体内容;也可以利用 append-to-body 参数改变默认挂载位置等等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值