博主封装了一个公共弹窗函数接收四个参数,(title:弹窗标题, ContentComponent:弹窗中显示的组件内容, opt:接收弹窗本身的属性和props, beforeSure:点击确定做的操作(请求后端接口))
import { defineComponent, h, ref, getCurrentInstance } from "vue-demi";
import Vue from "vue";
import { isFunction, isUndefined, noop, isPlainObject } from "lodash";
const WRAPPED = "wrapped";
function generateDialogComponent(wrapped, opt, listeners = {}) {
return defineComponent({
setup() {
const loading = ref(false);
const vm = getCurrentInstance();
const visible = opt.visible; // Ref
const closeable = opt.closeable; // Ref
const showCancelButton = isUndefined(opt.dialog.showCancelButton)
? true
: opt.dialog.showCancelButton;
const showSureBtn =
isUndefined(opt.dialog.showSureBtn) &&
isUndefined(opt.dialog.showSureButton)
? true
: opt.dialog.showSureBtn || opt.dialog.showSureButton;
const showFooter = isUndefined(opt.dialog.showFooter)
? true
: opt.dialog.showFooter;
const confirmButtonText = opt.dialog.confirmButtonText || "确定";
const cancelButtonText = opt.dialog.cancelButtonText || "取消";
return () => {
const sure = listeners.sure || (() => Promise.resolve());
const cancel = listeners.cancel || noop;
const destroy = listeners.destroy || noop;
// footer
const sureBtn = h(
"el-button",
{
props: {
size: "mini",
type: "primary",
loading: loading.value,
},
on: {
click: () => {
loading.value = true;
const wrappedVm = vm.proxy.$refs[WRAPPED];
return sure(wrappedVm)
.then(() => {
visible.value = false;
})
.finally(() => (loading.value = false));
},
},
},
confirmButtonText
);
const cancelBtn = h(
"el-button",
{
props: {
size: "mini",
},
on: {
click: () => {
visible.value = false;
},
},
},
cancelButtonText
);
const footer = h(
"div",
{
slot: "footer",
style: {
display: "flex",
justifyContent: "space-between",
},
},
[
h("div", [opt.dialog?.leftFooter?.()]),
h("div", [
closeable.value && showCancelButton && cancelBtn,
showSureBtn && sureBtn,
]),
]
);
return h(
"el-dialog",
{
props: {
closeOnClickModal: false,
visible: visible.value,
...opt.dialog,
closeOnClickModal: closeable.value,
closeOnPressEscape: closeable.value,
showClose: closeable.value,
},
on: {
"update:visible": (val) => {
visible.value = val;
},
closed: () => {
cancel();
destroy();
},
},
},
[
h("div", { style: { padding: "20px" } }, [
h(wrapped, {
ref: WRAPPED,
attrs: Object.assign({}, opt.props),
on: {
close: sure, // 组件内部可以通过 emit('close') 来关闭弹窗
},
}),
]),
showFooter && footer,
]
);
};
},
});
}
function openDialog(title, ContentComponent, opt, beforeSure) {
const defaultOpt = {
dialog: {},
props: {},
};
// 参数格式化
if (isUndefined(opt)) {
opt = defaultOpt;
}
if (isFunction(opt)) {
opt = defaultOpt;
beforeSure = opt;
}
if (!isFunction(beforeSure)) {
beforeSure = (vm) => vm.submit?.();
}
if (isPlainObject(opt)) {
if (isUndefined(opt.props)) {
opt = {
...opt,
props: opt,
};
}
}
opt.dialog = opt.dialog || opt || {};
opt.dialog.title = title;
const mountComponent = ($vueconfig) => {
const vm = new Vue($vueconfig);
const anchor = document.createElement("div");
document.body.appendChild(anchor);
vm.$mount(anchor);
return () => {
vm.$destroy();
document.body.removeChild(vm.$el);
};
};
// 控制 dialog 显隐
const visible = ref(false);
const closeDialog = () => {
visible.value = false;
};
// 控制是否可以关闭
const closeable = ref(true);
// 不可关闭弹窗
const freeze = () => {
closeable.value = false;
};
// 可关闭弹窗
const unfreeze = () => {
closeable.value = true;
};
const wait = new Promise((resolve, reject) => {
let disposer = null;
const destroy = () => isFunction(disposer) && disposer();
const cancel = () => {
reject(new Error("cancel"));
};
const sure = async (wrappedComp) => {
const promise = await beforeSure(wrappedComp);
resolve(promise);
};
disposer = mountComponent(
generateDialogComponent(
ContentComponent,
{ ...opt, visible, closeable },
{ sure, cancel, destroy }
)
);
// 打开弹窗
setTimeout(() => (visible.value = true), 20);
});
return {
close: closeDialog,
promise: wait,
freeze,
unfreeze,
};
}
export function pickByDialog(...args) {
const { promise } = openDialog(...args);
return promise;
}
/**
* 让 pickByDialog 静默失败, 并且提供一个手动关闭弹窗的函数
* @Returns { close }
*/
export const openByDialog = (...args) => {
const { close, freeze, unfreeze, promise } = openDialog(...args);
promise.catch(() => {
// no throw error
});
return {
close,
freeze,
unfreeze,
};
};
generateDialogComponent
函数解释:generateDialogComponent
函数定义了一个组件,并返回该组件。setup
函数中,首先定义了一些变量和常量,包括:loading
:一个用于表示加载状态的响应式变量(Ref)。vm
:当前组件实例的引用。visible
:一个响应式变量,表示对话框的可见性。closeable
:一个响应式变量,表示对话框是否可关闭。showCancelButton
:一个布尔值,表示是否显示取消按钮,默认为true
。showSureBtn
:一个布尔值,表示是否显示确定按钮,默认为true
。showFooter
:一个布尔值,表示是否显示底部内容,默认为true
。confirmButtonText
:确定按钮的文本,默认为"确定"。cancelButtonText
:取消按钮的文本,默认为"取消"。el-dialog
组件,该组件是一个基于Element UI库的对话框组件。el-dialog
组件的属性包括:closeOnClickModal
:控制是否在点击模态框时关闭对话框,根据closeable
的值进行设置。visible
:控制对话框的可见性,根据visible
的值进行设置。...opt.dialog
:将opt.dialog
对象中的所有属性都作为el-dialog
组件的属性。closeOnClickModal
:控制是否在点击模态框时关闭对话框,根据closeable
的值进行设置。closeOnPressEscape
:控制是否在按下Esc键时关闭对话框,根据closeable
的值进行设置。showClose
:控制是否显示关闭按钮,根据closeable
的值进行设置。el-dialog
组件的事件包括:update:visible
:当对话框的可见性发生变化时,更新visible
的值。closed
:当对话框关闭时,触发cancel
和destroy
函数。el-dialog
组件的插槽包括:{ padding: "20px" }
的div
元素,其中包含了wrapped
组件。showFooter
为true
时,底部插槽:包含一个具有样式{ display: "flex", justifyContent: "space-between" }
的div
元素,其中包含了底部内容。openDialog
的函数解释:openDialog
函数接受四个参数:title
(对话框标题),ContentComponent
(对话框内容组件),opt
(可选配置对象),和beforeSure
(可选的确定按钮点击前的回调函数)。defaultOpt
的默认配置对象,包含dialog
和props
两个属性。opt
和beforeSure
进行格式化处理:opt
为undefined
,则将其设置为defaultOpt
的值。opt
为函数,则将其设置为defaultOpt
的值,并将beforeSure
设置为该函数。beforeSure
不是函数,则将其设置为一个默认函数,该函数会调用传入的组件实例的submit
方法(如果存在)。opt
进行进一步处理:opt
是普通对象且没有props
属性,则将opt
的值复制给opt.props
。opt.dialog
设置为opt
或opt.dialog
的值,并将title
设置为opt.dialog.title
。mountComponent
的函数,用于将组件挂载到DOM上,并返回一个销毁函数。div
元素上。div
元素添加到document.body
中。document.body
中移除该div
元素。visible
,用于控制对话框的显示和隐藏。closeDialog
函数,用于关闭对话框。closeable
,用于控制对话框是否可以关闭。freeze
函数,将closeable
设置为false
,使对话框不可关闭。unfreeze
函数,将closeable
设置为true
,使对话框可关闭。wait
,用于等待对话框的操作完成。wait
的执行函数中,定义了一些内部函数和变量:disposer
:用于存储销毁函数的引用。destroy
函数:用于执行销毁函数。cancel
函数:用于拒绝Promise并抛出一个取消错误。sure
函数:在确定按钮点击时执行的回调函数,调用beforeSure
函数并传入wrappedComp
作为参数,并将返回的Promise解析为resolve
的值。disposer
设置为调用mountComponent
函数,并传入generateDialogComponent
函数生成的对话框组件。setTimeout
延迟20毫秒后,将visible
设置为true
,打开对话框。close
:关闭对话框的方法。promise
:返回等待对话框操作完成的Promise对象。freeze
:使对话框不可关闭的方法。unfreeze
:使对话框可关闭的方法。const { close } = openByDialog(
"自定义列表",
Setting2,
{
props: menuProps,
dialog: {
width: "980px",
confirmButtonText: "保存",
leftFooter: () =>
h(
"el-button",
{
style: { color: "#2783fe" },
props: {
size: "mini",
type: "text",
loading: reseting.value,
},
on: {
click: () => doReset(),
},
},
"恢复系统默认设置"
),
},
},
async (componentWrapper) => {
const updatedColumns = await componentWrapper?.updateColumnSetting();
this.emitChangeColumns2(updatedColumns);
}
);
//组件弹窗测试
async doImportLog() {
const [cancel, blob] = await to(
pickByDialog(
"弹窗导出测试",
getLogComp(this), //这个是组件
{
dialog: { width: "35%" },
// props: { value: this.value },
// on: {
// input: (val) => {
// this.value = val
// },
// },
},
async (vm) => {
const [changeDateBt, changeDateEt] = vm.date;
console.log("changeDateBt", changeDateBt);
console.log("changeDateEt", changeDateEt);
}
)
);
if (cancel) {
this.$message.info(this.$tof("cancel"));
return;
}
const curDate = dayjs().format("YYYYMMDD");
console.log("curDate", curDate);
saveAs(blob.data, this.$tof("file_name", { curDate }));
},
function getLogComp(vm) {
return {
data() {
return {
date: [],
};
},
render(h) {
return h("el-row", { style: { margin: "100px 50px" } }, [
h(
"el-col",
{ style: { lineHeight: "36px" }, attrs: { span: 8 } },
"导出范围时间"
),
h("el-col", { attrs: { span: 16 } }, [
h("el-date-picker", {
attrs: {
type: "daterange",
rangeSeparator: "-",
startPlaceholder: "开始日期",
endPlaceholder: "结束日期",
value: this.date,
valueFormat: "yyyy-MM-dd",
},
style: { width: "auto !important" },
on: {
input: (val) => {
this.date = val;
},
},
}),
]),
]);
},
};
}
//组件弹窗测试222
async doAdd() {
const [cancel] = await to(
pickByDialog(
"新增客户",
GroupCreate, //组件
{
dialog: {
width: "80%",
confirmButtonText: "保存",
leftFooter: () =>
h(
"el-button",
{
style: { color: "#2783fe" },
props: {
size: "mini",
type: "text",
loading: false,
},
on: {
click: () => doReset(),
},
},
"恢复系统默认设置"
),
},
props: {},
},
async (vm) => {
console.log("测试点击确定", vm);
}
)
);
if (cancel) {
this.$message.info("已取消");
return;
}
function doReset() {
console.log("测试左边操作");
}
},
GroupCreate组件:
搜索
{{ component }}
async doAdd2() {
openByDialog(
"测试表单",
FormTest,
{
props: {
isShowCol: true,
},
},
async (componentWrapper) => {
await componentWrapper?.$children[0].validate();
console.log("componentWrapper的数据", componentWrapper);
componentWrapper.ruleForm.date1 = dayjs(
componentWrapper.ruleForm.date1
).format("YYYYMMDD");
let payload = componentWrapper.ruleForm;
return await this.fetchData(payload);
}
);
},
FormTest组件
-
立即创建
模拟一个异步返回
fetchData(params) {
return new Promise((resolve, reject) => {
// 模拟异步请求
setTimeout(() => {
const data = { name: "John", age: 30, ...params };
// 模拟请求成功
resolve(data);
// this.$message.error("请求接口失败");
// 模拟请求失败
// reject('请求失败');
}, 1000);
});
},
填写完必填项发起后端请求,拿到数据