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

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

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

直接上代码:

MyDialog.vue 弹框组件

javascript 复制代码
<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

javascript 复制代码
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 表单组件

javascript 复制代码
<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 组件中调用

javascript 复制代码
<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>

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

相关推荐
passerby606119 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了26 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅29 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
吹牛不交税2 小时前
admin.net-v2 框架使用笔记-netcore8.0/10.0版
vue.js·.netcore