生成基于 addDialog 函数的命令式弹框
我们通常在列表页内实现弹框。
参考文件
你的参考上下文为:
- 命令式弹框源码: @apps\admin\src\views\components\dialog\index.vue
- 弹框案例: @https://pure-admin.github.io/vue-pure-admin/#/components/dialog
使用命令式弹框的组件:
- @apps\admin\src\pages\property-manage\expense-manage\expense-item-setting\index.vue
- @apps\admin\src\pages\operation-team\data-manage\property-management-company\index.vue
请务必阅读上述文件。
与生成表单组件高耦合
根据我提供给你的截图,来生成表单组件。注意,表单组件和命令式弹框,是高度耦合的。在你生成命令式弹框时,也应该同时开始生成表单组件。
主动为新增或编辑按钮增加打开弹框函数
在你生成命令式弹框的函数时,你也应该主动的将打开弹框的函数,应用到按钮上。比如新增按钮和编辑按钮。
使用 useMode 组合式 api 实现模式控制
每一个弹框表单,都需要实现模式控制。根据模式来做出不同的行为。典型的是根据新增或编辑模式,动态更新弹框标题或调用不同的接口。
比如该函数:
const { mode, modeText, setMode, isAdd, isEdit } = useMode();- useMode 是全局导入的函数,请直接使用。不需要你手动导入。
固定编写一段测试的异步函数
在生成命令式弹框的前面,你固定地使用以下代码段,实现一个模拟的异步操作。
const [isLoadingT, setIsLoadingT] = useToggle(false);
async function testAsync() {
setIsLoadingT(true);
consola.log("模拟异步操作, isLoadingT ", isLoadingT.value);
await sleep(1300);
setIsLoadingT(false);
consola.log("模拟异步操作, isLoadingT ", isLoadingT.value);
}- useToggle 是全局导入的组合式 api 函数,不需要你手动导入,请直接使用。
弹框组件实例
比如以下例子:
import { type ExpenseItemSettingFormProps, defaultForm } from "./components/form";
import ExpenseItemSettingForm from "./components/form.vue";
const expenseItemSettingFormInstance = ref<InstanceType<typeof ExpenseItemSettingForm> | null>(null);- 使用相对路径的方式,导入表单组件。该组件将作为弹框组件的实例。
- 在导入完表单后,随即新建弹框组件的实例。
- 实例必须预备好实例的类型,使用 typescript 的工具类型
InstanceType来获得到实例类型。
严格的 addDialog 打开弹框函数传参
实现命令式弹框,最重要的就是二次封装 addDialog 函数。该函数有一系列严格的传参,请阅读以下内容。
默认弹框属性 defaultAddDialogParams
每一个弹框都包含有这些默认属性,请直接使用以下的方式实现解构赋值。
addDialog({
...defaultAddDialogParams,
});- 全局导入:该变量 defaultAddDialogParams 是全局导入的,你不需要考虑如何导入该对象。
- 必须写在第一行:该变量包含了很多预设的变量,故需要以解构赋值的方式,写在第一行。便于其他可能的覆盖。
- 不需要重复定义:该变量是全局导入的,请不要再重复定义一次。
弹框标题 title
弹框标题往往是动态变化的,请你使用组合式 api useMode 函数来实现标题动态变化。其中,useMode 是全局自动导入的函数,不需要你手动导入。
如以下模板所示:
const { modeText } = useMode();
/** 打开弹框 */
function openDialog() {
/** 弹框标题 */
const title = `${modeText.value}费用项设置`;
addDialog({
title,
});
}如果标题不是动态变化的,是固定的,也请定义单独的 title 常量。
/** 打开弹框 */
function openDialog() {
/** 弹框标题 */
const title = `费用项设置`;
addDialog({
title,
});
}弹框属性 props
即被渲染组件的 props。一般是表单组件的 props。
如以下模板所示:
- 以相对路径的方式,获取到表单组件的 props 属性类型。
- 获取弹框组件的默认表单对象 defaultForm。
- 组装 formProps 时,务必对传入的值做一次深克隆 cloneDeep。其中,cloneDeep 是全局自动导入的函数,不需要你手动导入。
- 在 addDialog 函数中传入 props 对象。
import { type ExpenseItemSettingFormProps, defaultForm } from "./components/form";
function openDialog() {
/** 表单组件需要的props */
const formProps: ExpenseItemSettingFormProps = {
form: cloneDeep(defaultForm),
defaultValues: cloneDeep(defaultForm),
};
/** 弹框组件所需的变量 */
const props = formProps;
addDialog({
props,
});
}弹框渲染函数 contentRenderer
命令式弹框渲染组件,使用的是 vue 的渲染函数语法。
具体写法如下例子所示:
- ExpenseItemSettingForm 是通过相对路径导入的被渲染组件。通常是表单组件。
- expenseItemSettingFormInstance 是被渲染组件的组件实例。
- formProps 是该组件全部的 props 对象。
addDialog({
contentRenderer: () =>
h(ExpenseItemSettingForm, {
ref: expenseItemSettingFormInstance,
...formProps,
}),
});关闭回调函数 doBeforeClose
弹框关闭时,必须提供通用的关闭回调函数,写法几乎是固定的。
如下例子:
function openDialog() {
/** 表单组件需要的props */
const formProps: ExpenseItemSettingFormProps = {
form: cloneDeep(defaultForm),
defaultValues: cloneDeep(defaultForm),
};
/** 弹框组件所需的变量 */
const props = formProps;
/** 根据不同模式下 变化的表单默认重置对象 */
const defaultValues = props.defaultValues;
addDialog({
async doBeforeClose({ options, index }) {
const formComputed = expenseItemSettingFormInstance.value.formComputed;
await useDoBeforeClose({ defaultValues, formComputed, index, options });
},
});
}其中,expenseItemSettingFormInstance 是弹框组件的实例。要从弹框组件内获取固定对外导出的 formComputed 变量。
在你实际生成时,请你替换为实际的弹框组件实例。
useDoBeforeClose 函数是全局导入的函数,不需要你手动导入。
弹框底部按钮栏 footerButtons
请严格按照我提供给你的模板。编排按钮的位置、样式、和其他固定的交互函数。
如下例子:
const config = {
footerButtons: [
{
label: transformI18n($t("common.buttons.cancel")),
type: "info",
btnClick: async ({ dialog: { options, index }, button }) => {
// console.log(options, index, button);
const formComputed = expenseItemSettingFormInstance.value.formComputed;
await useDoBeforeClose({ defaultValues, formComputed, index, options });
},
},
{
label: transformI18n($t("common.buttons.reset")),
type: "warning",
btnClick: ({ dialog: { options, index }, button }) => {
// 手动重置表单
expenseItemSettingFormInstance.value.plusFormInstance.handleReset();
},
},
{
label: transformI18n($t("common.buttons.submit")),
type: "success",
btnClick: async ({ dialog: { options, index }, button }) => {
// 提交表单时 校验
const res = await expenseItemSettingFormInstance.value.plusFormInstance.handleSubmit();
if (res) {
button.btn.loading = true;
await testAsync();
button.btn.loading = false;
closeDialog(options, index);
}
},
},
],
};严格的按钮排布顺序
- 取消按钮
- 重置按钮
- 提交按钮
该顺序不能错乱。
取消按钮
取消按钮的固定模板如下:
const footerButtons = [
{
label: transformI18n($t("common.buttons.cancel")),
type: "info",
btnClick: async ({ dialog: { options, index }, button }) => {
const formComputed = expenseItemSettingFormInstance.value.formComputed;
await useDoBeforeClose({ defaultValues, formComputed, index, options });
},
},
];请注意将 expenseItemSettingFormInstance 替换成实际的表单组件实例。
重置按钮
重置按钮的固定模板如下:
const footerButtons = [
{
label: transformI18n($t("common.buttons.reset")),
type: "warning",
btnClick: ({ dialog: { options, index }, button }) => {
expenseItemSettingFormInstance.value.plusFormInstance.handleReset();
},
},
];请注意将 expenseItemSettingFormInstance 替换成实际的表单组件实例。
提交按钮
提交按钮的固定模板如下:
const footerButtons = [
{
label: transformI18n($t("common.buttons.submit")),
type: "success",
btnClick: async ({ dialog: { options, index }, button }) => {
const res = await expenseItemSettingFormInstance.value.plusFormInstance.handleSubmit();
if (res) {
button.btn.loading = true;
await testAsync();
button.btn.loading = false;
closeDialog(options, index);
}
},
},
];- 请注意将 expenseItemSettingFormInstance 替换成实际的表单组件实例。
- 请注意,务必先生成好固定的,测试的异步函数。测试用的异步函数在此处使用。