从0到1解锁Element-Plus组件二次封装El-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"], // 注入managerrender() {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; // 直接在实例上设置managerreturn 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: {     // 这里可以传入组件的propsmessage: '这是来自父组件的消息',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传递给对话框组件 。再结合provideinject,它们就像是一座桥梁,能够让不同层级的组件之间方便地进行通信,让数据的传递更加灵活 。

实际应用场景

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

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

总结

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构实验习题

codeblock F2是出控制台 1.1 /* by 1705 WYY */ #include <stdio.h> #include <stdlib.h> #define TRUE 1 #define FALSE 0 #define YES 1 #define NO 0 #define OK 1 #define ERROR 0 #define SUCCESS 1 #define UNSUCCESS 0 #define OVERFLOW -2 #define UNDERF…

PyTorch 2.7深度技术解析:新一代深度学习框架的革命性演进

引言:站在AI基础设施变革的历史节点 在2025年这个充满变革的年份,PyTorch团队于4月23日正式发布了2.7.0版本,随后在6月4日推出了2.7.1补丁版本,标志着这个深度学习领域最具影响力的框架再次迎来了重大突破。这不仅仅是一次常规的版本更新,而是一次面向未来计算架构和AI应…

LTspice仿真10——电容

电路1中电容下标m5&#xff0c;表示5个该电阻并联电路2中ic1.5v&#xff0c;表示电容初始自带电量&#xff0c;电压为1.5v

C#事件驱动编程:标准事件模式完全指南

事件驱动是GUI编程的核心逻辑。当程序被按钮点击、按键或定时器中断时&#xff0c;如何规范处理事件&#xff1f;.NET框架通过EventHandler委托给出了标准答案。 &#x1f50d; 一、EventHandler委托&#xff1a;事件处理的基石 public delegate void EventHandler(object se…

全面的 Spring Boot 整合 RabbitMQ 的 `application.yml` 配置示例

spring:rabbitmq:# 基础连接配置 host: localhost # RabbitMQ 服务器地址port: 5672 # 默认端口username: guest # 默认用户名password: guest # 默认密码virtual-host: / # 虚拟主机&#xff08;默认/&…

Win32 API实现串口辅助类

近期需要使用C++进行串口通讯,将Win32 API串口接口进行了下封装,可实现同步通讯,异步回调通讯 1、SerialportMy.h #pragma once #include <Windows.h> #include <thread> #include <atomic> #include <functional> #include <queue> #inclu…

Python-执行系统命令-subprocess

1 需求 2 接口 3 示例 4 参考资料 Python subprocess 模块 | 菜鸟教程

Web攻防-XMLXXE上传解析文件预览接口服务白盒审计应用功能SRC报告

知识点&#xff1a; 1、WEB攻防-XML&XXE-黑盒功能点挖掘 2、WEB攻防-XML&XXE-白盒函数点挖掘 3、WEB攻防-XML&XXE-SRC报告 一、演示案例-WEB攻防-XML&XXE-黑盒功能点挖掘 1、不安全的图像读取-SVG <?xml version"1.0" standalone"yes&qu…

浏览器工作原理37 [#] 浏览上下文组:如何计算Chrome中渲染进程的个数?

一、前言 在默认情况下&#xff0c;如果打开一个标签页&#xff0c;那么浏览器会默认为其创建一个渲染进程。 如果从一个标签页中打开了另一个新标签页&#xff0c;当新标签页和当前标签页属于同一站点&#xff08;相同协议、相同根域名&#xff09;的话&#xff0c;那么新标…

位置编码和RoPE

前言 关于位置编码和RoPE 应用广泛&#xff0c;是很多大模型使用的一种位置编码方式&#xff0c;包括且不限于LLaMA、baichuan、ChatGLM等等 第一部分 transformer原始论文中的标准位置编码 RNN的结构包含了序列的时序信息&#xff0c;而Transformer却完全把时序信息给丢掉了…

手动使用 Docker 启动 MinIO 分布式集群(推荐生产环境)

在生产环境中&#xff0c;MinIO 集群通常部署在多个物理机或虚拟机上&#xff0c;每个节点运行一个 MinIO 容器&#xff0c;并通过 Docker 暴露 API 和 Console 端口。 1. 准备工作 假设有 4 台服务器&#xff08;也可以是同一台服务器的不同端口模拟&#xff0c;但不推荐生产…

如何在IntelliJ IDEA中设置数据库连接全局共享

在现代软件开发中&#xff0c;数据库连接管理是开发过程中不可或缺的一部分。为了提高开发效率&#xff0c;减少配置错误&#xff0c;并方便管理&#xff0c;IntelliJ IDEA 提供了一个非常有用的功能&#xff1a;数据库连接全局共享。通过这个功能&#xff0c;你可以在多个项目…

【Python】文件应用: 查找读取的文件内容

查找读取的文件内容 from pathlib import Pathpath Path(pi_million_digits.txt) contents path.read_text()lines contents.splitlines() pi_string for line in lines:pi_string line.lstrip()birthday input("Enter your birthday, in the form mmddyy: "…

交互式剖腹产手术模拟系统开发方案

以下是为您设计的《交互式剖腹产手术模拟系统》开发方案框架,包含技术实现路径与详细内容结构建议。由于篇幅限制,这里呈现核心框架与关键模块说明: 交互式剖腹产手术模拟系统开发方案 一、项目背景与意义 1.1 传统医学教学痛点分析 尸体标本成本高昂(约$2000/例)活体训…

AWS WebRTC: 判断viewer端拉流是否稳定的算法

在使用sdk-c viewer端进行拉流的过程中&#xff0c;viewer端拉取的是视频帧和音频帧&#xff0c;不会在播放器中播放&#xff0c;所以要根据收到的流来判断拉流过程是否稳定流畅。 我这边采用的算法是&#xff1a;依据相邻帧之间的时间间隔是否落在期望值的 20% 范围内。 音频…

【Python】文件读取:逐行读取应用实例——从一个JSONL文件中逐行读取文件

从一个JSONL文件中逐行读取文件&#xff0c;并将这些问题保存到一个新的JSONL文件中 import json import argparse import os # 导入os模块用于检查文件是否存在def read_questions_from_jsonl(file_path, limit):"""从JSONL文件中读取指定数量的question部分…

百宝箱生成智能体

点击新建应用 工作流如下&#xff1a; 点击发布 点击Web服务&#xff0c;上架

嵌入式 数据结构学习(五) 栈与队列的实现与应用

一、栈(Stack)详解 1. 栈的基本概念 栈的定义与特性 后进先出(LIFO)&#xff1a;最后入栈的元素最先出栈 操作限制&#xff1a;只能在栈顶进行插入(push)和删除(pop)操作 存储位置&#xff1a;我们实现的链栈位于堆区(malloc分配)&#xff0c;系统栈区存储函数调用信息 栈…

汇编与接口技术:8259中断实验

一、实验目的 该实验使学生掌握8259向量中断方式的硬件连接和软件编程的方法&#xff0c;同时使同学掌握中断和其它接口芯片配合来完成某一特定任务的方法。 二、实验内容 1、手动产生单脉冲作为中断请求信号连接到MIRQ3上和SIRT10上。每按一次开关产生一次中断&#xff0c;…

Ajax的初步学习

一、什么是 Ajax&#xff1f; Ajax (Asynchronous JavaScript and XML) 是一种无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。通过在后台与服务器进行少量数据交换&#xff0c;Ajax 可以使网页实现异步更新。 主要特性&#xff1a; 异步性 (Asynchronous…