vue3封装命令式弹窗组件

一、前言

相信大家都封装过弹窗组件,基本思路都是父组件给子组件传递一个变量,子组件props进行接收,当点击确认或者关闭按钮时,通过emit回传事件供父组件调用。这种封装方式缺点是复用性查、使用频繁时需要定义多份{isVisible、handleSubmit、handleClose}代码,代码冗余,今天分享一种命令式组件封装的方式。

二、什么是命令式组件

命令式组件封装是一种将功能封装在组件内部,并通过命令式的方式进行调用和控制的封装方法。在命令式组件封装中,组件负责封装一定的功能逻辑,并提供一组接口或方法,供外部代码调用来触发和控制组件的行为。调用方式大致为:

async function showMsg(){
    const [,res]= await msgBox('测试内容',{type:'success',iconType:'question'})
    if(res){
        consloe.log('点击了确定')
    }else{
        console.log('点击了取消')
    }
}

三、开始封装

首先创建一个MessageBox.vue文件,编写组件样式代码,这里我使用的是unocss进行编写。

<script lang="ts" setup>
import { computed, onMounted, ref } from "vue"
import { ElButton, ElDialog } from 'element-plus'
export interface HuiMsgBoxProp {
  /** 控制图标展示类型 info:叹号 success:钩 question:问号 */
  iconType:'info' | 'success' | 'question',
  /** 控制图标展示的颜色 */
  type:'info' | 'warning' | 'success' | 'danger',
  /** 弹窗显示的内容 */
  content:string,
  /** 取消按钮的文本 */
  cancelText:string,
  /** 确定按钮的文本 */
  confirmText:string,
  /** 关闭事件 */
  closeBox: ()=> void,
  /** 确定事件事件 */
  confirmHandler:()=> void,
  /** 取消事件 */
  cancelHandler:()=> void,
}
const { iconType, type, content, cancelText, confirmText, closeBox, confirmHandler, cancelHandler } = withDefaults(defineProps<HuiMsgBoxProp>(), {
  iconType: 'info',
  type: 'info',
  cancelText: '取消',
  confirmText: '确定',
})

const iconTypeClass = computed<string>(() => {
  const iconTypeClassList = {
    info: 'text-disabled',
    warning: 'text-warning',
    success: 'text-success',
    danger: 'text-danger',
  }
  return iconTypeClassList[type]
})

const iconColorClass = computed<string>(() => {
  const iconColorClassList = {
    info: 'i-com-gantanhao',
    success: 'i-com-gou1',
    question: 'i-com-wenhao',
  }
  return iconColorClassList[iconType]
})

// 控制显示处理
const isVisible = ref(false)
/**
 * 组件展示
 */
const show = () => {
  isVisible.value = true
}

/**
 * 处理动画 (render 函数的渲染,会直接进行)
 */
onMounted(() => {
  show()
})

/**
 * 取消事件
 */
const onCancelClick = () => {
  if (cancelHandler) {
    cancelHandler()
  }
  close()
}

/**
 * 确定事件
 */
const onConfirmClick = () => {
  if (confirmHandler) {
    confirmHandler()
  }
  closeBox()
}

// 关闭动画处理时间
const duration = '0.5s'
/**
 * 关闭事件,保留动画执行时长
 */
const close = () => {
  isVisible.value = false
  // 延迟一段时间进行关闭
  setTimeout(() => {
    if (closeBox) {
      closeBox()
    }
  }, parseInt(duration.replace('0.', '').replace('s', '')) * 100)
}
</script>

<template>
  <div class="hua5-message-box">
    <ElDialog
      v-model="isVisible"
      width="400"
      @closed="close"
    >
      <div class="flex justify-center flex-center h-110">
        <div>
          <i :class="[iconTypeClass,iconColorClass,'icon-com !text-27']" />
        </div>
        <div class="text-14 font-bold text-normal ml-11">{{ content }}</div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <ElButton class="!text-primary !border !border-1 !border-primary" @click="onCancelClick">{{ cancelText }}</ElButton>
          <ElButton type="primary" class="w-100px" @click="onConfirmClick">{{ confirmText }}</ElButton>
        </div>
      </template>
    </ElDialog>
  </div>
</template>

<style lang="scss">
.hua5-message-box{
  .el-dialog{
    border-radius: 8px !important;
  }
  .el-dialog .el-dialog__header{
    background-color: #fff !important;
  }
  .el-dialog .el-dialog__footer{
    background-color: #fff !important;
  }
}
</style>

组件需要接收参数

export interface HuiMsgBoxProp {
  /** 控制图标展示类型 info:叹号 success:钩 question:问号 */
  iconType:'info' | 'success' | 'question',
  /** 控制图标展示的颜色 */
  type:'info' | 'warning' | 'success' | 'danger',
  /** 弹窗显示的内容 */
  content:string,
  /** 取消按钮的文本 */
  cancelText:string,
  /** 确定按钮的文本 */
  confirmText:string,
  /** 关闭事件 */
  closeBox: ()=> void,
  /** 确定事件事件 */
  confirmHandler:()=> void,
  /** 取消事件 */
  cancelHandler:()=> void,
}

其次创建index.ts文件,由于组件调用方式是通过一个函数进行调用的,并提供.then.catch方法,所以需要编写一个函数,该函数返回一个Promise。当调用该函数,创建组件实例,组件进行挂载。

import { h, render } from 'vue'
import confirmComponent from './message-box.vue'
import { to } from "@hua5/hua5-utils"

export interface PayLoadType {
  /** 控制图标展示类型 info:叹号 success:钩 question:问号 */
  iconType?:'info' | 'success' | 'question',
  /** 控制图标展示的颜色 */
  type?: "info" | "success" | "danger" | "warning",
  /** 取消按钮的文本 */
  cancelText?:string,
  /** 确定按钮的文本 */
  confirmText?:string
}
export const hua5MsgBox = (content: string, payLoad:PayLoadType = {}) => {
  const { iconType = 'info', type = 'info', cancelText, confirmText } = payLoad
  return new Promise((resolve) => {
    // 取消按钮事件
    const cancelHandler = () => {
      resolve(false)
    }

    // 确定按钮事件
    const confirmHandler = () => {
      resolve(true)
    }

    // 关闭弹层事件
    const closeBox = () => {
      render(null, document.body)
    }

    // 1. 生成 vnode
    const vnode = h(confirmComponent, {
      content,
      iconType,
      type,
      cancelText,
      confirmText,
      cancelHandler,
      confirmHandler,
      closeBox,
    })

    // 2. render 渲染
    render(vnode, document.body)
  })
}

export const msgBox = (content: string, payLoad?:PayLoadType) => {
  return to(hua5MsgBox(content, payLoad))
}

四、to函数

/**
 * @param { Promise } promise
 * @param { Object= } errorExt - Additional Information you can pass to the err object
 * @return { Promise }
 */
export function to(promise: Promise<any>): Promise<any> {
  return promise.then(res => [null, res]).catch(error => [error, null])
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值