当前位置: 首页 > news >正文

从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.lryc.cn/news/579618.html

相关文章:

  • Unity-Shader-几何着色器
  • 学习设计模式《十六》——策略模式
  • Linux 73 LAMP4
  • 离线迁移 Conda 环境到 Windows 服务器:用 conda-pack 摆脱硬路径限制
  • 从0开始学习R语言--Day37--CMH检验
  • VR 果蔬运输开启农业物流新变革
  • AI无标记动捕如何结合VR大空间技术打造沉浸式游戏体验
  • 从0到1实战!用Docker部署Qwerty Learner输入法的完整实践过程
  • https如何利用工具ssl证书;使用自己生成的证书
  • 创建 TransactionStatus
  • rabbitmq 与 Erlang 的版本对照表 win10 安装方法
  • Debian-10-standard用`networking`服务的`/etc/network/interfaces`配置文件设置多网卡多IPv6
  • 贝叶斯深度学习:赋予AI不确定性感知的认知革命
  • 日本IT|日本做后端开发需要具备什么技能开发经验?
  • 深入理解CSS中的BFC 与IFC , 布局的两大基础概念
  • Day50 预训练模型+CBAM模块
  • 【Python】图像识别的常用功能函数
  • golang json omitempty 标签研究
  • 服务器如何配置防火墙规则开放/关闭端口?
  • 数据库运维指导书
  • 74. 搜索二维矩阵
  • WPS 如何使用宏录制功能
  • Web 服务器架构选择深度解析
  • 【字节跳动】数据挖掘面试题0006:SVM(支持向量机)详细原理
  • LiteHub中间件之跨域访问CORS
  • 【ArcGISPro】基于Pro的Python环境进行Django简单开发Web
  • 队列和栈数据结构
  • RabbitMQ 高级特性之发送方确认
  • NV133NV137美光固态闪存NV147NV148
  • c++中的绑定器