在vue3中使用tsx结合render封装一个项目内通用的弹窗组件

场景: 在大屏项目中经常需要用到弹窗的需求,通常一个项目内弹窗的样式是一致的,如果不封装一个弹窗组件hook的话,每一个弹窗都需要单独封装为一个组件然后再放到项目layout的最外层,再通过store全局判断哪个弹窗显示,这样显然很麻烦,所以现在我封装一个通过tsx及render渲染弹窗组件的hooks,需要使用弹窗就调用hooks传入弹窗组件就行

思路: 首先封装一个弹窗基础框架组件(包括弹窗外观样式及关闭和底部操作等事件)提供插槽展示每个弹窗的内容部分,然后在需要用到一个弹窗时封装这个弹窗的内容组件,然后调用hooks传入这个内容组件就可以打开弹窗(在封装的hooks中会通过render将弹窗基础组件和内容组件合并渲染出来生成一个元素节点,再通过document.body.appendChild将生成的元素节点挂载到页面中以实现弹窗的展示效果)

代码步骤:

(1)封装弹窗组件框架

<template>
  <el-dialog class="models-default" v-bind="props" :model-value="visible">
    <slot></slot>
    <!-- 弹窗头部插槽 -->
    <template v-if="$slots.header" #header>
      <slot name="header"></slot>
    </template>
    <!-- 弹窗尾部插槽 -->
    <template v-if="isFooter" #footer>
      <!-- 自定义尾部插槽组件显示 -->
      <template v-if="$slots.footer">
        <slot name="footer"></slot>
      </template>
      <!-- 默认尾部按钮列显示 -->
      <template v-else>
        <el-button @click="visible = false">{{ props.cancelBtnText }}</el-button>
        <el-button v-for="(item, index) in footerButtons" :key="index" :icon="item.icon" :type="item.type"
          @click="() => btnClickHandle(item)">{{ item.name }}</el-button>
        <el-button type="primary" @click="confirmHandle">{{ props.confirmBtnText }}</el-button>
      </template>
    </template>
  </el-dialog>
</template><script lang="ts" setup>
import { ref, getCurrentInstance } from 'vue'
import { ElDialog, ElButton, type DialogProps, type ButtonProps } from 'element-plus'const emits = defineEmits(['update:modelValue', 'confirm', 'beforeClose'])
// 传入的弹窗props
const props = withDefaults(defineProps<BProps>(), {
  footerButtons: () => [], //底部按钮
  confirmBtnText: '确认',  //底部确认按钮文字
  cancelBtnText: '取消',   //底部取消按钮文字
  isFooter: true,   //是否展示底部按钮
  showClose: true,  //是否展示关闭图标
  title: '弹窗名称', //弹窗标题
})const visible = ref(true)const instance = getCurrentInstance()const closeModel = (value = false) => {
  visible.value = value
}const confirmHandle = () => {
  emits('confirm', instance?.proxy?.$refs[props.contentRef], closeModel)
}const btnClickHandle = (item: FooterButtons) => {
  // item.onClick && item?.onClick(instance?.proxy?.$refs[props.contentRef], closeModel)
  item.onClick?.(instance?.proxy?.$refs[props.contentRef], closeModel)
}
</script><script lang="ts">
// 约束底部按钮
export interface FooterButtons {
  icon?: string
  name?: string
  type?: ButtonProps['type']
  onClick?: (contentInstance: any, done: () => void) => void
}
// ts接口约束弹窗接收外部的props
interface BProps extends Partial<DialogProps> {
  footerButtons?: FooterButtons[]
  confirmBtnText?: string
  cancelBtnText?: string
  isFooter?: boolean
  contentRef: string
}
</script>
<style lang="scss">
 // 省略样式
</style>

(2)封装tsx渲染弹窗组件的hook(通过tsx将弹窗框架以及传入的弹窗内容组件结合并渲染到页面上)


import type { ComponentInternalInstance, Ref } from 'vue'
import { h, render, onUnmounted, ref } from 'vue'
import { type DialogProps } from 'element-plus'
import type { JSX } from 'vue/jsx-runtime'
// 弹窗框架组件
import Model, { type FooterButtons } from '@/components/models/index.vue'
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'type Content = Parameters<typeof h>[0] | string | JSX.Element
​
interface UseModelProps extends Partial<DialogProps> {
  isFooter?: boolean // 是否展示底部
  footerButtons?: Array<FooterButtons> // 底部按钮(除取消和保存按钮之外的按钮)
  onConfirm?: (contentInstance: any) => void
  onClosed?: () => void
  onOpened?: () => void
}// 弹窗插槽暴露
interface ElDialogSlots {
  header?: () => JSX.Element
  footer?: () => JSX.Element
}interface ContentPropsType {
  ref: string
  [key: string]: any
}interface Options {
  props: UseModelProps // model props
  slots?: ElDialogSlots // 弹窗插槽对象
  contentProps?: ContentPropsType // 弹窗主体插入逐渐props
}/**
 * 窗体模块hooks
 * @param {Object} content 设置弹窗主体组件对象
 * @param {Object} options 配置信息
 * @param {Object} options.props 窗体组件props,继承element-puls dialog组件的所有props
 * @param {Boolean} options.props.isFooter 是否显示底部内容
 * @param {Array} options.props.footerButtons 底部按钮添加
 * @param {Function} options.props.onConfirm 确认按钮回调
 * @param {Function} options.props.onClosed 弹窗关闭回调
 * @param {Function} options.props.onOpened 弹窗打开回调
 * @param {Function} options.slots 弹窗插槽对象
 * @param {Object} options.contentProps 弹窗主体插入组件props
 * @returns
 */
export function useModel (content: Content, options?: Options) {
  // 弹窗组件实例
  const modelInstance: Ref<ComponentInternalInstance | null> = ref(null)
  // 弹窗的元素节点
  let fragment: Element | null = null// 关闭并卸载组件
  const closeAfter = () => {
    if (fragment) {
      render(null, fragment as unknown as Element) // 卸载组件
      fragment.textContent = '' // 清空文档片段
      fragment = null
    }
    modelInstance.value = null
  }
  // 关闭弹窗
  function close () {
    if (modelInstance.value) modelInstance.value.props.modelValue = false
  }
 // 核心代码
  function open () {
    // 打开弹窗前判断如果存在当前弹窗实例就销毁掉重新创建
    if (modelInstance.value) {
      close()
      closeAfter()
    }
    const { props = {}, slots = {}, contentProps = { ref: 'content' } } = options ?? {}
    // 创建元素节点
    fragment = document.createDocumentFragment() as unknown as Element
    // tsx组件内容(将弹窗框架组件和传入的内容组件整合)
    const vNode = (
      <ElConfigProvider locale={zhCn} size="small" zIndex={3000}>
        {/* 将传入的props弹窗配置信息都传入到弹窗框架组件中 */}
        <Model align-center {...props} modelValue={true} contentRef={contentProps.ref}>
          {{
            {/* 弹窗内容组件 */}
            default: () => <content {...contentProps}></content>,
            ...slots,
          }}
        </Model>
      </ElConfigProvider>
    )
    // render将tsx生成的组件vNode渲染到元素节点上
    render(vNode, fragment)
    // 弹窗实例
    modelInstance.value = vNode.component
    // 将弹窗的元素节点挂载到页面上
    document.body.appendChild(fragment)
  }onUnmounted(() => {
    close()
  })
  
  // 将外部需要用到的方法和变量暴露出去
  return {
    open, // 打开弹窗
    close, // 关闭弹窗
    closeAfter, // 销毁弹窗
    modelInstance, // 弹窗实例
  }
}

(3)在需要用弹窗组件的地方使用封装的hooks


// 弹窗hooks
import { useModel } from '@/hooks/useModel'
// 弹窗内容组件
import ShopDetail from '../detailDialog/ShopDetail.vue';const showDetail = () => {
  // 传入弹窗内容组件及弹窗组件需要用到的props参数
  const { open, closeAfter } = useModel(ShopDetail, {
    // 弹窗框架组件props
    props: {
      title: '店铺详情',
      width: '900px',
      isFooter: false,
      // 关闭弹窗回调
      onClosed: () => {
        closeAfter()
      }
    },
    // 弹窗内容组件props
    contentProps: {
      ref: 'detail',
      dataInfo: props.markerData
    },
  })
  // 打开弹窗
  open()
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/943974.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电脑配置maven-3.6.1版本

不要使用太高的版本。 apache-maven-3.6.1-bin.zip 下载这个的maven压缩包 使用3.6.1版本。 解压缩放在本地软甲目录下面&#xff1a; 配置系统环境变量 在系统环境下面配置MAVEN_HOME 点击path 新增一条 在cmd中输入 mvn -v 检查maven的版本 配置阿里云镜像和本地的仓库 …

Python基础语法知识——数据类型的查询、数据类型转化

今天第一次学习python&#xff0c;之前学习过C&#xff0c;感觉学习起来还可以&#xff0c;就是刚用的时候有点手残&#xff0c;想的是python代码&#xff0c;结果写出来就是C,本人决定每天抽出时间写点。同时继续更新NX二次开发专栏学习&#xff0c;话不多说&#xff0c;晚上的…

Boost之log日志使用

不讲理论&#xff0c;直接上在程序中可用代码&#xff1a; 一、引入Boost模块 开发环境&#xff1a;Visual Studio 2017 Boost库版本&#xff1a;1.68.0 安装方式&#xff1a;Nuget 安装命令&#xff1a; #只安装下面几个即可 Install-package boost -version 1.68.0 Install…

C语言初阶习题【17】求N的阶乘( 递归和非递归实现)

1.题目 2.分析 非递归 需要用到循环&#xff0c;n个数就是循环n次&#xff0c;每次和之前的乘起来 例如 5的阶乘 就是 5*4 *3 *2 *1 循环1到5 。需要一个变量来接收每次的结果 注意这个地方是乘&#xff0c;所以要从1 开始&#xff0c;sum 也需要是1而不是0 for(i 1&#xf…

云效流水线自动化部署web静态网站

云效流水线部署静态网站 背景新建流水线配置流水线运行流水线总结 背景 配置流水线以前&#xff0c;每次更新导航网站都要登进去宝塔后台&#xff0c;删掉旧的目录和文件&#xff0c;再上传最新的文件&#xff0c;太麻烦啦 网上的博客基本都是分享vue项目&#xff0c;这一篇是…

【开源项目】数字孪生化工厂—开源工程及源码

飞渡科技数字孪生化工厂管理平台&#xff0c;基于自研孪生引擎&#xff0c;将物联网IOT、人工智能、大数据、云计算等技术应用于化工厂&#xff0c;为化工厂提供实时数据分析、工艺优化、设备运维等功能&#xff0c;助力提高生产效率以及提供安全保障。 通过可视化点位标注各厂…

SpringCloud整合skywalking实现链路追踪和日志采集

1.部署skywalking https://blog.csdn.net/qq_40942490/article/details/144701194 2.添加依赖 <!-- 日志采集 --><dependency><groupId>org.apache.skywalking</groupId><artifactId>apm-toolkit-logback-1.x</artifactId><version&g…

Linux下Nvidia显卡GPU开启驱动持久化

GPU开启驱动持久化的原因 GPU 驱动一直处于加载状态&#xff0c; 减少运行程序时驱动加载的延迟。不开启该模式时&#xff0c;在程序每次调用完 GPU 后&#xff0c; GPU 驱动都会被卸载&#xff0c;下次调用时再重新加载&#xff0c; 驱动频繁卸载加载&#xff0c; GPU 频繁被…

图像处理-Ch4-频率域处理

Ch4 频率域处理(Image Enhancement in Frequency Domain) FT &#xff1a;将信号表示成各种频率的正弦信号的线性组合。 频谱&#xff1a; ∣ F ( u , v ) ∣ [ R 2 ( u , v ) I 2 ( u , v ) ] 1 2 |F(u, v)| \left[ R^2(u, v) I^2(u, v) \right]^{\frac{1}{2}} ∣F(u,v)…

虚拟化 | Proxmox VE 8.x 开源的虚拟化平台快速上手指南

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ] 0x00 简介说明 前言简述 描述:作为一个爱折腾的IT打工佬,时刻以学习各类新技术新知识为目标,这不正好有一台部署了VMware vSphere ESXi 虚拟化环境的服务器,由于正好安装其系统的磁盘有坏道,经常导致使用 ESXi 异…

rocketmq-push模式-消费侧重平衡-类流程图分析

1、观察consumer线程 使用arthas分析 MQClientFactoryScheduledThread 定时任务线程 定时任务线程&#xff0c;包含如下任务&#xff1a; 每2分钟更新nameServer列表 每30秒更新topic的路由信息 每30秒检查broker的存活&#xff0c;发送心跳请求 每5秒持久化消费队列的offset…

使用亚马逊针对 PyTorch 和 MinIO 的 S3 连接器实现可迭代式数据集

2023 年 11 月&#xff0c;Amazon 宣布推出适用于 PyTorch 的 S3 连接器。适用于 PyTorch 的 Amazon S3 连接器提供了专为 S3 对象存储构建的 PyTorch 数据集基元&#xff08;数据集和数据加载器&#xff09;的实现。它支持用于随机数据访问模式的地图样式数据集和用于流式处理…

[2003].第2-01节:关系型数据库表及SQL简介

所有博客大纲 后端学习大纲 MySQL学习大纲 1.数据库表介绍&#xff1a; 1.1.表、记录、字段 1.E-R&#xff08;entity-relationship&#xff0c;实体-联系&#xff09;模型中有三个主要概念是&#xff1a; 实体集 、 属性 、 联系集2.一个实体集&#xff08;class&#xff09…

wps透视数据表

1、操作 首先选中你要的行字段表格 -> 插入 -> 透视数据表 -> 拖动行值&#xff08;部门&#xff09;到下方&#xff0c;拖动值&#xff08;包裹数量、运费&#xff09;到下方 2、删除 选中整个透视数据表 -> delete 如图&#xff1a;

Python-流量分析常用工具脚本(Tshark,pyshark,scapy)

免责声明&#xff1a;本文仅作分享~ 目录 wireshark scapy 例&#xff1a;分析DNS流量 检查数据包是否包含特定协议层&#xff08;过滤&#xff09; 获取域名 例&#xff1a;提取 HTTP 请求中的 Host 信息 pyshark 例&#xff1a;解析 HTTP 请求和响应 例&#xff1a;分…

开发场景中Java 集合的最佳选择

在 Java 开发中&#xff0c;集合类是处理数据的核心工具。合理选择集合&#xff0c;不仅可以提高代码效率&#xff0c;还能让代码更简洁。本篇文章将重点探讨 List、Set 和 Map 的适用场景及优缺点&#xff0c;帮助你在实际开发中找到最佳解决方案。 一、List&#xff1a;有序存…

[2029].第6-06节:MyISAM引擎中的索引与 InnoDB引擎中的索引对比

所有博客大纲 后端学习大纲 MySQL学习大纲 1.MyISAM索引&#xff1a; 1.1.B树索引适用存储引擎&#xff1a; 1.B树索引适用存储引擎如下表所示&#xff1a; 2.即使多个存储引擎都支持同一种类型的B树索引&#xff0c;但它们的实现原理也是不同的 Innodb和MyISAM默认的索引是B…

DS的使用

使用DS和[address]实现字的传送 要解决的问题:CPU从内存单元中要读取数据 要求&#xff1a;CPU要读取一个内存单元的时候&#xff0c;必须先给出这个内存单元的地址。 原理&#xff1a;在8086PC中&#xff0c;内存地址段地址和偏移地址组成(段地址:偏移地址) 解决方案 :DS和[a…

使用RKNN进行YOLOv8人体姿态估计的实战教程:yolov8-pose.onnx转yolov8-pose.rknn+推理全流程

之前文章有提到“YOLOv8的原生模型包含了后处理步骤,其中一些形状超出了RK3588的矩阵计算限制,因此需要对输出层进行一些裁剪”,通过裁剪后得到的onnx能够顺利的进行rknn转换,本文将对转rnkk过程,以及相应的后处理进行阐述。并在文末附上全部源码、数据、模型的百度云盘链…

短视频矩阵系统后端源码搭建实战与技术详解,支持OEM

一、引言 随着短视频行业的蓬勃发展&#xff0c;短视频矩阵系统成为了众多企业和创作者进行多平台内容运营的有力工具。后端作为整个系统的核心支撑&#xff0c;负责处理复杂的业务逻辑、数据存储与交互&#xff0c;其搭建的质量直接影响着系统的性能、稳定性和可扩展性。本文将…