需求:封裝一個表單彈框組件,彈框和表單是兩個組件,表單組件以插槽的形式動態傳入彈框組件中。
外部組件使用的方式如下:
直接上代碼:
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>
----------------------------------------------------------分隔線----------------------------------------------------------
?