从0到1解锁Element-Plus组件二次封装El-Dialog动态调用

vue3二次封装elementPlus的dialog弹窗组件 公共dialog:暴露一个方法,控制组件的状态 阅读详情

技术难题初登场

家人们,最近在开发一个超复杂的后台管理系统项目,里面有各种数据展示、表单提交、权限控制等功能,在这个过程中,我频繁地使用到了element-plus组件库中的el-dialog组件 。它就像一个小弹窗,可以用来显示各种提示信息、编辑表单之类的。比如说在用户点击 “编辑” 按钮时,就弹出一个el-dialog,里面放着编辑表单,让用户修改数据。

但随着功能的不断完善,我发现原生的el-dialog组件在某些场景下真的不够灵活。比如说,我想在不同的组件中根据不同的业务逻辑动态地控制对话框的显示和隐藏,还要传递不同的数据给对话框,原生组件用起来就很麻烦,每次都要写很多重复的代码,这可太影响开发效率了。所以,我就决定对el-dialog进行二次封装,实现动态调用,让它更好地满足我的项目需求。接下来,就把我的经验分享给大家,一起看看怎么解决这个问题。

实现效果

在这里插入图片描述

代码实现

import { ElButton, ElDialog } from "element-plus";
import { createApp, h } from "vue";

// 创建一个 DialogManager 类来管理对话框
class DialogManager {
  constructor() {
    this.dialogs = [];
  }

  /**
   * 创建并显示一个动态对话框
   * @param {Object} options - 对话框配置选项
   * @returns {Object} - 返回对话框实例
   */
  create(options = {}) {
    // 合并默认配置
    const defaultOptions = {
      title: "提示",
      visible: true,
      fullscreen: false,
      top: "15vh",
      modal: true,
      lockScroll: true,
      closeOnClickModal: true,
      closeOnPressEscape: true,
      beforeClose: null,
      footerBtns: [
        {
          label: "取消",
          type: "default",
          handler: (instance) => {
            instance.close();
          },
        },
        {
          label: "确定",
          type: "primary",
          handler: (instance) => {
            instance.close();
          },
        },
      ],
    };

    const dialogOptions = { ...defaultOptions, ...options };

    // 创建一个容器元素
    const container = document.createElement("div");
    document.body.appendChild(container);

    // 创建 Dialog 组件实例
    const app = createApp({
      data() {
        return {
          dialogVisible: dialogOptions.visible,
        };
      },
      provide() {
        return {
          manager: this.$options.manager, // 提供manager
        };
      },
      inject: ["manager"], // 注入manager
      render() {
        const footerNodes = dialogOptions.footerBtns.map((btn, index) => {
          return h(
            ElButton,
            {
              key: index,
              type: btn.type || "default",
              size: "small",
              onClick: () => {
                if (btn.handler) {
                  btn.handler(this);
                }
              },
            },
            () => btn.label
          );
        });

        // 改进 content 渲染方式
        const renderContent = () => {
          if (typeof dialogOptions.content === "string") {
            return h("div", { class: "dialog-content" }, dialogOptions.content);
          } else if (dialogOptions.content) {
            // 只传递必要的属性和方法
            const props = {
              // 提供关闭对话框的回调
              closeDialog: () => {
                this.close();
              },
              // 可以添加其他必要的属性
            };

            // 如果有额外的 props,合并它们
            if (dialogOptions.contentProps) {
              Object.assign(props, dialogOptions.contentProps);
            }

            return h(dialogOptions.content, props);
          } else {
            return h("div", { class: "dialog-content" }, "No content provided");
          }
        };

        return h(
          ElDialog,
          {
            title: dialogOptions.title,
            modelValue: this.dialogVisible,
            "onUpdate:modelValue": (val) => {
              this.dialogVisible = val;
            },
            fullscreen: dialogOptions.fullscreen,
            top: dialogOptions.top,
            modal: dialogOptions.modal,
            lockScroll: dialogOptions.lockScroll,
            closeOnClickModal: dialogOptions.closeOnClickModal,
            closeOnPressEscape: dialogOptions.closeOnPressEscape,
            beforeClose: dialogOptions.beforeClose,
            onClose: () => {
              this.dialogVisible = false;
              if (dialogOptions.onClose) {
                dialogOptions.onClose(this);
              }
              // 延迟销毁,让关闭动画完成
              setTimeout(() => {
                this.destroy();
              }, 300);
            },
          },
          {
            // 默认插槽用于对话框内容
            default: renderContent,
            footer: () =>
              h(
                "div",
                {
                  class: "dialog-footer",
                },
                footerNodes
              ),
          }
        );
      },
      methods: {
        // 关闭对话框
        close() {
          this.dialogVisible = false;
        },
        // 销毁对话框实例
        destroy() {
          app.unmount();
          if (container.parentNode) {
            container.parentNode.removeChild(container);
          }

          // 从管理列表中移除
          if (this.manager) {
            const index = this.manager.dialogs.indexOf(this);
            if (index !== -1) {
              this.manager.dialogs.splice(index, 1);
            }
          }
        },
      },
      mounted() {
        // 添加到管理列表
        if (this.manager) {
          this.manager.dialogs.push(this);
        } else {
          console.error("Manager is not initialized");
        }
      },
    }).provide("manager", this); // 全局提供manager

    // 挂载应用
    const instance = app.mount(container);
    instance.manager = this; // 直接在实例上设置manager

    return instance;
  }

  /**
   * 关闭所有对话框
   */
  closeAll() {
    this.dialogs.forEach((dialog) => {
      dialog.close();
    });
  }
}

// 创建单例实例
const dialogManager = new DialogManager();

// 导出创建对话框的函数
export function createDialog(options) {
  return dialogManager.create(options);
}

// 导出关闭所有对话框的函数
export function closeAllDialogs() {
  dialogManager.closeAll();
}

调用示例

-------------------------------- 基本文本对话框 -------------------------------------
import { createDialog } from '@/utils/dialog-manager';

export default {
  methods: {
    showSimpleDialog() {
      createDialog({
        title: '确认操作',
        content: '你确定要执行这个操作吗?',
        onClose: () => {
          console.log('对话框已关闭');
        },
        footerBtns: [
          {
            label: '取消',
            handler: (instance) => {
              console.log('点击了取消');
              instance.close();
            },
          },
          {
            label: '确认',
            type: 'primary',
            handler: (instance) => {
              console.log('执行确认操作');
              instance.close();
            },
          },
        ],
      });
    },
  },
};


-------------------------- 包含组件的对话框 --------------------------------
import { createDialog } from '@/utils/dialog-manager';
import MyComponent from '@/components/MyComponent.vue';

export default {
  methods: {
    showComponentDialog() {
      createDialog({
        title: '自定义组件对话框',
        content: MyComponent,
        contentProps: {     // 这里可以传入组件的props
          message: '这是来自父组件的消息',
          src: 'xxx',
        },
        width: '600px',
        footerBtns: [
          {
            label: '关闭',
            handler: (instance) => {
              instance.close();
            },
          },
        ],
      });
    },
  },
};

-------------------------- 异步操作对话框 --------------------------------

import { createDialog } from '@/utils/dialog-manager';

export default {
  methods: {
    async showAsyncDialog() {
      const dialog = createDialog({
        title: '正在加载...',
        content: '数据加载中,请稍候...',
        showClose: false, // 隐藏关闭按钮
        footerBtns: [],   // 不显示底部按钮
      });

      try {
        // 模拟异步操作
        const result = await this.fetchData();
        dialog.close();

        // 显示成功消息
        createDialog({
          title: '操作成功',
          content: '数据加载完成',
          footerBtns: [
            {
              label: '确定',
              type: 'primary',
              handler: (instance) => instance.close(),
            },
          ],
        });
      } catch (error) {
        dialog.close();

        // 显示错误消息
        createDialog({
          title: '操作失败',
          content: `错误: ${error.message}`,
          footerBtns: [
            {
              label: '关闭',
              handler: (instance) => instance.close(),
            },
          ],
        });
      }
    },
  },
};

原理剖析

这里面的原理其实也不难理解,主要是利用了Vue的响应式原理 。我们都知道,在Vue中,数据发生变化时,视图会自动更新。我们封装的组件就是基于这个特性,通过一个响应式的变量来控制el-dialog的显示与隐藏。比如说,我们定义一个isDialogVisible变量,当它为true时,el-dialog就显示,为false时就隐藏 。

而动态传递参数呢,则是通过props来实现的。我们在封装的组件中定义好props,然后在调用组件的时候,就可以把需要的数据通过props传递进去,这样对话框就能根据不同的数据展示不同的内容啦 。比如说,我们要在对话框里显示不同的提示信息,就可以把提示信息作为props传递给对话框组件 。再结合provide和inject,它们就像是一座桥梁,能够让不同层级的组件之间方便地进行通信,让数据的传递更加灵活 。

实际应用场景

经过二次封装实现动态调用的el-dialog在实际项目中的应用场景可太广泛了 。比如说在用户信息编辑场景,当用户点击 “编辑” 按钮,就可以动态弹出我们封装好的对话框,里面填充好用户当前的信息,用户修改完成后点击确认,就能提交新的信息,整个过程非常流畅自然 。

在文件上传确认场景,当用户选择好文件准备上传时,弹出对话框让用户确认文件信息,比如文件名、文件大小等 。如果没问题再点击上传,这样可以避免用户误操作上传错误的文件 。还有在权限管理中,当管理员要给某个用户分配新的权限时,通过动态调用对话框,展示所有权限选项,管理员勾选后提交,就能完成权限分配,操作简单又高效 。

总结

经过二次封装实现动态调用的el-dialog组件,不仅大大提高了代码的复用性,让我们在不同的业务场景中能够轻松应对对话框的各种需求,还增强了项目的灵活性和可维护性。以前写一堆重复代码的日子一去不复返啦!

强烈建议各位小伙伴在自己的项目中也尝试应用这种二次封装的方法 ,相信你们会发现它的强大之处。要是在实践过程中有什么经验,或者遇到了问题,都欢迎在评论区留言分享。咱们一起交流,共同进步 !

从手机到自动驾驶:MIPI-CSI2协议演进史与车规级改造要点 本文追溯了MIPI-CSI2协议从智能手机摄像头到自动驾驶核心传感器的演进历程。重点剖析了该协议在车规级改造中面临的严苛挑战,包括物理层稳定性、EMC防护、功能安全与数据可靠性等关键要点,并探讨了其在多传感器融合与高带宽实时传输中的工程实践。 阅读详情

相关推荐

SSL证书验证:VMware Horizon Client的安全隐患与临时解决方案

本文深入分析了VMware Horizon Client中SSL证书验证失败的安全隐患,提供了临时解决方案与长期防御策略。针对自签名证书、证书链断裂等常见问题,文章详细解析了风险成因,并给出分级响应方案,帮助企业平衡安全与效率。通过实战案例和自动化脚本,指导IT管理员构建主动防御体系,有效降低中间人攻击风险。

weixin_29290863的博客 337

Element Plus使用el-dialog

【代码】Element Plus使用el-dialog。

qq_34631220的博客 1266

YIUI框架深度解析:基于ET的数据驱动UI开发实践

在Unity游戏开发中,UI管理是构建复杂应用的核心挑战之一。传统命令式编程需要开发者手动操作每个UI组件,导致业务逻辑与视图层高度耦合,代码难以维护。数据驱动是一种先进的架构思想,它通过建立数据模型与UI组件之间的绑定关系,实现数据变化时UI的自动更新,从而将开发者的关注点从“如何操作UI”转移到“数据状态是什么”。这种模式显著提升了代码的可维护性和开发效率,尤其适用于状态频繁变化的游戏界面。YIUI框架正是这一思想在Unity UGUI上的工程化实践,它通过Model-View-Component的清晰

java天气 431

ElementPlus弹出对话框dialog

主要介绍使用component标签导入外部组件,以及通过校验规则判定是否关闭对话框

delete_you的博客 4700

【ArcGIS Add-in编程】如何创建ArcMap的下拉菜单插件

step1. VS2010中创建一个空的解决方案step2.创建一个项目 直接点击finish。step3 为该项目创建项目,选择Menu,作为根菜单,而后修改xml中的items项,添加需要放在下拉菜单中的项目,例如一个按钮; step4 现在可以创建项目,作为菜单中的项目。为该项目添加新的项目,选择button 此时 xml文件中,多了该button的项目,将该项目添加到items

吃货小跟班的博客 4431

力扣(Leetcode)第1题:两数之和(C语言版本)

题目: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。 示例 1: 输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。 示例 2: 输入:nums = [3,2,4], target = 6

qq_39855356的博客 3309

Element Plus封装el-dialog、el-table、el-select表格对话框,实现单选多选场景。

Element Plus 封装表格对话框组件,实现单选以及多选的场景。

m0_67584973的博客 4005

vietjetair 航空的webpack多参数生成学习

工作内不是特别多,居安思危,有一个抓机票信息的公司面试前竟然需要做面试题,这就是一个题。看了下,有点麻烦,webpack 最初不得要领,看了下资料,找了个框架,练了两个简单的,搞这个吧。入库需要自己找,有几个参数不是明文的,算法不是很复杂,位置不太好找。整体难度一般。

liu_yueyang的博客 1510

前端开发攻略---在Vue3中对ElementPlus中的dialog组件进行二次封装

在Vue3中对ElementPlus中的dialog组件进行二次封装

nibabaoo的博客 2222

YOLOv8【第十二章:数据增强与数据工程篇·第10节】主动学习(Active Learning):如何挑选最有价值的样本进行标注!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 508

[关系图谱] 二.Gephi导入共线矩阵构建作者关系图谱

本文主要讲解Gephi绘制作者间的关系图谱,该软件可以广泛应用于社交网络、知识图谱分析,推荐读者使用。这是非常基础的一篇文章,重点讲解Gephi使用方法,希望对大家有所帮助。

杨秀璋的专栏 1万+

针对element ui plus 的el-dialog问题说明和二次开发

针对element ui plus 的el-dialog问题说明和二次开发

qq_36400043的博客 1642

4、教育数据挖掘:公共数据集与研究进展

本文综述了教育数据挖掘(EDM)领域的研究进展与公共数据集应用情况。从2013年至2020年,EDM在电子学习系统、大规模开放在线课程(MOOC)等方面快速发展,涵盖了多种研究任务、工具与方法。文章梳理了UCI、Mendeley、DataShop等平台的公开数据集,分析了其特点与局限性,并介绍了KDD Cup、Kaggle等竞赛中的教育数据集及其评价指标。同时,探讨了多模态、心理行为等特色数据集的应用场景。最后指出当前EDM面临的主要挑战是缺乏系统性数据集综述、数据预处理复杂及可重复性不足,呼吁加强数据共享

rgv2345678的博客 171

vue3 element-plus el-dialog 二次封装,多层调用

el-dialog

MFWSCQ的博客 4282

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

基于element-plus的el-dialog二次封装

本文章旨在提供思路参考,可根据具体项目需求进行封装。类名为添加自定义样式,可根据需求添加自己的样式。代码如下: 属性可参照。

shengjon的博客 2516

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

vue3+element-plus Dialog对话框的使用 与 setup 写法的使用

/ 定义控制弹窗显隐的变量。"监听flag值得变化:"// 接受父组件传过来的值。'你点击了确定按钮''你点击了确定按钮''你点击了确定按钮'// 将变量暴露出来。

bigpatten的博客 1万+

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Element Plus对话框(ElDialog)全面指南:打造灵活弹窗交互

📌 开篇导语对话框是Web应用中实现用户交互的核心组件之一,常用于信息确认、表单提交或详情展示。Element Plus的ElDialog组件以高扩展性和优雅动效著称,支持高度定制化开发。本文将从基础配置到进阶技巧,手把手教你掌握对话框组件的使用精髓。 2. 按需引入对话框组件 ✅三、基础用法:快速实现弹窗 1. 控制对话框显示与隐藏 通过v-model绑定显示状态: 使用#header插槽覆盖默认标题: 2. 全屏模式与宽度调整 3. 可拖拽对话框 通过第三方库实现(

2401_83194332的博客 4122

vue 中 el-dialog 二次封装

(1)对element-plus的el-dialog进行二次封装,实现自定义对话框宽高、定位、底部按钮,(2)新增功能包括拉伸、全屏,(3)对原本API也带入了封装,包括遮罩层、关闭图标、打开关闭事件回调、按钮事件等。附:我将所有用到的API写在了最下面(第6条)

chouge的博客 2661

关于Vue3中,element-plus的el-dialog对话框无法显示的可能原因。

关于Vue3中,element-plus的el-dialog对话框无法显示的可能原因。

ky51336的博客 1万+

vue3 setup学习之[封装elementplus中的el-dialog弹窗]

弹窗和表单的封装,应方便业务使用,包括含有form表单的新增和编辑业务,入参包括各种表单元素、校验规则rules、标题等,配置好入参渲染即可使用。后台管理系统中,会经常性的出现各种增删改查业务,表单使用较多的地方。在业务页面中,引用封装好的PageModal组件。其中的modal.config.ts代码如下。PageModal.vue中type.ts。

fangshuiguang的博客 1431

自定义element-plus的弹框样式

项目中弹框使用频繁,需要统一样式风格,此组件可以自定义弹框的头部样式和内容

i-cat的博客 3703

elementPlus的dialog组件封装

公司业务可能需要进行一些组件的封装,基于第三方elementPlus框架,进行符合UI设计原型的组件封装,这篇主要讲解Dialog弹出框的封装。

outsider76557的博客 4291

vue3 element-plus中,el-dialog组件内,无法通过ref获得组件实例

vue3 element-plus中,el-dialog组件内,无法通过ref获得组件实例 。 对话框中的内容是懒加载的,这意味着在对话框打开之前,默认插槽(个人理解为el-dialog标签内的内容)不会被渲染到DOM上。因此,如果你需要执行一个DOM操作或通过ref访问组件,可在open事件回调中进行。经过测试,发现可以通过opened事件回调(open不行),可以访问el-dialog中组件的实例。...

hookind的博客 7315

记录--vue3优雅的使用element-plus的dialog

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 如何优雅的基于 element-plus,封装一个梦中情 dialog 优点 摆脱繁琐的 visible 的命名,以及反复的重复 dom。 想法 将 dialog 封装成一个函数就能唤起的组件。如下: addDialog({ title: "测试", //弹窗名 component: TestVue, //组件 wid...

林恒的博客 1542

vue3 element-plus中关于el-dialog

场景是使用el-dropdown和el-dropdown-menu封装了一个组件,trigger为hover类型,el-dropdown-menu的内容是封装了一个组件其中包含一个设置按钮,点击设置按钮弹出一个dialog弹出框,弹出框单独封装了一个vue组件,在修改弹出框样式的时候发现只要下拉菜单收起来之后弹窗也就消失了,具体的底层问题没研究只记录一下解决方法添加了如下三个属性就解决了消失的问题。记录一下使用el-dialog遇到的问题。

m0_60522381的博客 960

关于vue的elementUI和vue3的element-plus的el-dialog的样式以及el-table中的tooltip的样式修改(个人见解)

关于vue的elementUI和vue3的element-plus的el-dialog的样式以及el-table的tooltip的样式修改

qq_32154641的博客 1万+
上一篇: 玩机系列之:海外VPS、独立站备份神器GoBackup
下一篇: B 站又搞大事情,AniSora V3 震撼开源,一键开启动漫视频自由
hackchen
博客等级 码龄19年 193粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值