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>

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

相关推荐
大鱼前端17 分钟前
Vue 3.5 :新特性全解析与开发实践指南
vue.js
_龙衣1 小时前
将 swagger 接口导入 apifox 查看及调试
前端·javascript·css·vue.js·css3
进取星辰2 小时前
25、Tailwind:魔法速记术——React 19 样式新思路
前端·react.js·前端框架
x-cmd3 小时前
[250512] Node.js 24 发布:ClangCL 构建,升级 V8 引擎、集成 npm 11
前端·javascript·windows·npm·node.js
夏之小星星3 小时前
el-tree结合checkbox实现数据回显
前端·javascript·vue.js
crazyme_63 小时前
前端自学入门:HTML 基础详解与学习路线指引
前端·学习·html
撸猫7913 小时前
HttpSession 的运行原理
前端·后端·cookie·httpsession
亦世凡华、3 小时前
Rollup入门与进阶:为现代Web应用构建超小的打包文件
前端·经验分享·rollup·配置项目·前端分享
琉璃℡初雪4 小时前
vue2/3 中使用 @vue-office/docx 在网页中预览(docx、excel、pdf)文件
vue.js·pdf·excel
Bl_a_ck4 小时前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架