Vue2 elementUI 二次封装命令式表单弹框组件

news2025/5/16 7:41:21

需求:封装一个表单弹框组件,弹框和表单是两个组件,表单组件以插槽的形式动态传入弹框组件中。

外部组件使用的方式如下:

直接上代码:

MyDialog.vue 弹框组件

<template>
  <el-dialog
      :title=title
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
      width="40%">
    <slot name="content"></slot>
    <span slot="footer" class="dialog-footer">
      <el-button size="mini" @click="handleCancelClick">取 消</el-button>
      <el-button size="mini" type="primary" @click="handleOkClick">确 定</el-button>
    </span>
  </el-dialog>
</template>

<script>
export default {
  name: "MyDialog",
  props: {
    title: {
      type: String
    },
    message: {
      type: String
    },
    icon: {
      type: String,
      default: "info"
    },
    handleCancel: {
      type: Function
    },
    handleOk: {
      type: Function
    }
  },

  data() {
    return {
      dialogVisible: true,
    }
  },

  methods: {
    handleCancelClick() {
      this.dialogVisible = false;
      this.handleCancel();
    },
    handleOkClick() {
      this.dialogVisible = false;
      this.handleOk();
    },
    handleTestClick() {
      this.$emit('test-click')
    },
  }
}
</script>

<style scoped>
  /deep/.el-dialog__body {
    padding: 15px 20px;
  }

  /deep/ .el-dialog__header {
    padding: 2px 10px 2px;
    background-color: #1E2C3D;
    color: white;
  }
  /deep/ .el-dialog__title {
    color: white;
    font-size: 13px;
    font-family: 微软雅黑,serif;
  }

  /deep/ .el-dialog__headerbtn {
    top: 6px;
  }
  /deep/ .el-dialog__headerbtn .el-dialog__close {
    color: #fff;
  }

</style>

 MyDialog.js 

import Vue from 'vue';
import MyDialog from "@/components/dialog4/MyDialog.vue";
import EventBus from "@/lib/event-bus";

/**
 * 弹框组件的构造器
 * @param ctxCpm
 * @param dlgProps
 * @param onOkClick
 * @param onCancelClick
 * @returns {ExtendedVue<Vue, unknown, unknown, unknown, Record<never, any>, {}, ComponentOptionsMixin, ComponentOptionsMixin>|VNode}
 */
function getDialogConstructor(ctxCpm, dlgProps, onOkClick, onCancelClick) {
	return Vue.extend({
		render(h) {
			return h(
				MyDialog, {
					props: {
						...dlgProps,
						handleOk: onOkClick,
						handleCancel: onCancelClick
					}
				},
				[h(ctxCpm, {
					slot: 'content',
					ref: 'myform',
				},)
				]
			)
		}
	})
}

// 暴露此函数供外部组件调用
/**
 * 
 * @param ctxCpm     表单组件 
 * @param dlgProps   弹框组件的配置项props
 * @param onOkClick  确认按钮点击事件回调函数
 * @param onCancelClick 取消按钮点击事件回调函数
 * @returns {(function(): void)|*}  弹窗关闭后的回调函数
 */
export const useDialog = (ctxCpm, dlgProps, onOkClick, onCancelClick) => {
	let DialogConstructor = getDialogConstructor(ctxCpm, dlgProps, () => {
		EventBus.$emit('form-submit', {callback: (formData) => {
				onOkClick(formData);
		  }});
	}, onCancelClick);
	const dlg = new DialogConstructor();
	const dlgInstance = dlg.$mount();
	document.body.appendChild(dlgInstance.$el);
	return () => {
		dlgInstance.$el.remove();
		dlgInstance.$destroy();
		EventBus.$off("form-submit");		// 移除表单提交事件监听
	}
}

UserForm.vue 表单组件

<template>
  <el-form>
    <el-form-item label="用户名">
      <el-input v-model="form.name"></el-input>
    </el-form-item>
    <el-form-item label="年龄">
      <el-input v-model="form.age"></el-input>
    </el-form-item>
    <el-form-item label="住址">
      <el-input v-model="form.address"></el-input>
    </el-form-item>
  </el-form>
</template>

<script>
import EventBus from "@/lib/event-bus";

export default {
  name: "UserForm",
  props: {
    dlgProps: Object
  },
  data() {
    return {
      form: {
        name: '',
        age: 0,
        address: ''
      }
    }
  },
  methods: {
    takeFormData() {
      return {...this.form}
    }
  },
  created() {
    // 监听表单提交(确认按钮点击)
    EventBus.$on('form-submit', (p) => {
      p.callback(this.takeFormData());
    });
  }
}
</script>

<style scoped>

</style>

MyDialogTest.vue 组件中调用

<template>
  <div>
    <el-button @click="handleClick">点我弹出用户组件弹框</el-button>
  </div>
</template>

<script>
import {useDialog} from "@/components/dialog4/MyDialog";
import UserForm from "@/components/dialog4/UserForm";

export default {
  name: "MyDialogTest",
  methods: {
    handleClick() {
      const close = useDialog(UserForm, {title: "新增用户表单", message: "是否确定?", icon: "warn"}, (params) => {
        console.log("Test.....", params);
        close();
      }, () => {
        console.log("取消按钮被点击");
        close();
      })
    }
  }
}
</script>

<style scoped>

</style>

效果如下:

优化1: 

以上的写法中,是采用EventBus 事件总线的方式来获取表单提交的数据,也就是点击确认后提交表单,在onOkClick 处理函数中获取表单数据。后面想了想,可以再精简一点。

MyDialog.js

import Vue from 'vue';
import MyDialog from "@/components/dialog5/MyDialog.vue";

function getDialogConstructor(ctxCpm, dlgProps, onOkClick, onCancelClick) {
	return Vue.extend({
		render(h) {
			return h(
				MyDialog, {
					props: {
						...dlgProps,
						handleOk: onOkClick,
						handleCancel: onCancelClick
					}
				},
				[h(ctxCpm, {
					slot: 'content',
					ref: 'myform',
					props: {
						fdata: dlgProps.fdata
					}
				},)
				]
			)
		}
	})
}

export const useDialog = (ctxCpm, dlgProps, onOkClick, onCancelClick) => {
	let DialogConstructor = getDialogConstructor(ctxCpm, dlgProps, onOkClick, onCancelClick);
	const dlg = new DialogConstructor();
	const dlgInstance = dlg.$mount();
	document.body.appendChild(dlgInstance.$el);
	return () => {
		dlgInstance.$el.remove();
		dlgInstance.$destroy();
	}
}

这个文件的调整是,在h函数渲染表单组件ctxCpm时,通过props传入一个fdata。把事件总线的代码删了。

那么在表单组件中,就顶一个props fdata来接收。修改如下:

TestForm.vue

<template>
  <el-form>
    <el-form-item label="姓名">
      <el-input v-model="fdata.name"></el-input>
    </el-form-item>
    <el-form-item label="年龄">
      <el-input v-model="fdata.age"></el-input>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  name: "TestForm",
  props: ["fdata"],
}
</script>

<style scoped>

</style>

在TestForm.vue 组件中只需要定义一个props来接收即可。

然而,这样修改后,在外部组件中要调用 useDIalog 这个就需要传参数了。

MyDialogTest.vue

<template>
  <div>
    <el-button @click="handleClick">点我弹出弹框</el-button>
  </div>
</template>

<script>
import TestForm from "@/components/dialog5/TestForm";
import {useDialog} from "@/components/dialog5/MyDialog"
export default {
  name: "MyDialogTest5",
  data() {
    return {
      fdata: {}
    }
  },
  methods: {
    handleClick() {
      const close = useDialog(TestForm, {fdata: this.fdata}, () => {
        console.log({...this.fdata})
        close();
      }, () => {});
    }
  }
}
</script>

<style scoped>

</style>

----------------------------------------------------------分隔线----------------------------------------------------------

 

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

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

相关文章

Antd中Form详解:

1.获取Form表单值的方式: ① 使用Form.useForm()钩子&#xff08;推荐方式&#xff09; const [form] Form.useForm();const getFormValues () > {const values form.getFieldsValue();};<Form form{form}>...<Form.Item label{null}><Button onClick{ge…

docker系列-DockerDesktop报错信息(Windows Hypervisor is not present)

Docker Desktop 报错信息 Docker Desktop - Windows Hypervisor is not present Docker Desktop is unable to detect a Hypervisor. Hardware assisted virtualization and data execution protection must be enabled in the BIOS.这是因为 Docker Desktop 需要启用 虚拟化技…

《基于 Kubernetes 的 WordPress 高可用部署实践:从 MariaDB 到 Nginx 反向代理》

手把手教你用 Kubernetes 部署高可用 WordPress 博客 本实验通过 Kubernetes 容器编排平台&#xff0c;完整部署了一个高可用的 WordPress 网站架构&#xff0c;包含 MariaDB 数据库、WordPress 应用和 Nginx 反向代理三大核心组件。实验涵盖了从基础环境准备到最终服务暴露的…

Ubuntu源码版comfyui的安装

Comfyui也出桌面版了&#xff0c;但是想让大家多个人都使用怎么办呢&#xff1f;也有方法&#xff0c;安装Linux版&#xff0c;启动后会生成个网页地址&#xff0c;打开就能用了。 1、先来看下本地安装环境配置&#xff1a; 系统&#xff1a;Ubuntu 22.04 内存&#xff1a;2…

制作一款打飞机游戏47:跳转

编辑器的问题 我们开始为不同的敌人编写一些行为&#xff0c;到目前为止进展顺利&#xff0c;一切都很棒。但上次我们遇到了一些问题&#xff0c;我们发现在这个编辑器中编写代码有时有点困难&#xff0c;因为当你想要在某行之间插入内容时&#xff0c;你不得不删除一切然后重…

本地部署ollama及deepseek(linux版)

一、安装ollama export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download"curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/download|$OLLAMA_MIRROR|g" | shexport OLLAMA_MIRROR&q…

vue H5解决安卓手机软键盘弹出,页面高度被顶起

开发中安卓机上遇到的软键盘弹出导致布局问题 直接上代码_ 在这里插入代码片 <div class"container"><div class"appContainer" :style"{height:isKeyboardOpen? Heights :inherit}"><p class"name"><!-- 绑定…

CSS专题之自定义属性

前言 石匠敲击石头的第 12 次 CSS 自定义属性是现代 CSS 的一个强大特性&#xff0c;可以说是前端开发需知、必会的知识点&#xff0c;本篇文章就来好好梳理一下&#xff0c;如果哪里写的有问题欢迎指出。 什么是 CSS 自定义属性 CSS 自定义属性英文全称是 CSS Custom Proper…

七、深入 Hive DDL:管理表、分区与洞察元数据

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月13日 专栏&#xff1a;Hive教程 内容导航 一、表的 DDL 操作 (非创建)二、分区的 DDL 操作三、洞察元数据&#xff1a;SHOW 命令的威力结语&#xff1a;DDL 与 SHOW&#xff0c;Hive 管理的双翼练习题一、选择题二、代码题…

直接在Excel中用Python Matplotlib/Seaborn/Plotly......

本次分享如何利用pyxll包&#xff0c;实现直接在Excel中使用Python Matplotlib/Seaborn/Plotly等强大可视化工具。 pyxll配置 pyxll安装 pip install pyxll pyxll install pyxll自定义方法 例如&#xff0c;自定义一个计算斐波那契数的方法fib&#xff0c;并使用pyxll装饰器…

React面试常问问题详解

以下是30个React面试中常见的问题及简要解析&#xff0c;涵盖基础概念、核心原理、性能优化、Hooks、状态管理等方面&#xff0c;适用于初中高级开发者准备面试时参考&#xff1a; 一、React 基础与核心概念 React 是什么&#xff1f; React 是由 Facebook 开发的用于构建用户界…

【Java】网络编程(Socket)

网络编程 Socket 我们开发的网络应用程序位于应用层&#xff0c;TCP和UDP属于传输层协议&#xff0c;在应用层如何使用传输层的服务呢&#xff1f;在应用层和传输层之间&#xff0c;则使用套接字Socket来进行分离 套接字就像是传输层为应用层开的一个小口&#xff0c;应用程…

思科(Cisco ASA/Firepower)、华三(H3C)、华为(Huawei USG)防火墙 的基础配置

以下是针对 思科&#xff08;Cisco ASA/Firepower&#xff09;、华三&#xff08;H3C&#xff09;、华为&#xff08;Huawei USG&#xff09;防火墙 的基础配置指南&#xff0c;涵盖 区域划分、安全策略、NAT、路由 等核心功能。配置示例基于通用场景&#xff0c;实际部署时需根…

Windows环境下maven的安装与配置

1.检查JAVA_HOME环境变量 Maven是使用java开发的&#xff0c;所以必须知道当前系统环境中的JDK的安装目录。 搜索栏直接输入“cmd” 或者 WinR 输入cmd 在打开的终端窗口输入“echo %JAVA_HOME”&#xff0c;就可以看到jdk的位置了。 如果没有的话&#xff0c;请参考我的文章&a…

LeetCode:513、找树左下角的值

//递归法 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* t…

Vxe UI vue vxe-table 实现表格数据分组功能,不是使用树结构,直接数据分组

Vxe UI vue vxe-table 实现表格数据分组功能&#xff0c;不是使用树结构&#xff0c;直接数据分组 查看官网&#xff1a;https://vxetable.cn gitbub&#xff1a;https://github.com/x-extends/vxe-table gitee&#xff1a;https://gitee.com/x-extends/vxe-table 代码 通过…

如何禁止chrome自动更新

百度了一下 下面这个方法实测有效 目录 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable 3、验证是否禁止更新成功&#xff1a; 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable GoogleUpdater InternalService…

阳光学院【2020下】计算机网络原理-A卷-试卷-期末考试试卷

一、单选题&#xff08;共25分&#xff0c;每空1分&#xff09; 1.ICMP协议工作在TCP/IP参考模型的 ( ) A.主机-网络 B.网络互联层 C.传输层 D.应用层 2.下列关于交换技术的说法中&#xff0c;错误的是 ( ) A.电路交换适用于突发式通信 B.报文交换不能满足实时通信 C.报文…

kotlin-协程(什么是一个协程)

1.什么指一个协程对于线程来说一个thread就是就是指一个线程&#xff0c;thread为什么成为线程呢&#xff1f;因为他实现了对线程的一个抽象管理&#xff0c;可以管理这个线程&#xff0c;启动&#xff0c;可以查看各种信息 那么协程呢&#xff1f; public fun CoroutineScop…

数组和切片的区别

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…