web/src/components/form/form-modal.tsx

198 lines
5.2 KiB
TypeScript

import { Button, ButtonInstance, FormInstance, Message, Modal } from "@arco-design/web-vue";
import { assign, cloneDeep, omit } from "lodash-es";
import { PropType, VNode, defineComponent } from "vue";
import { Form } from "./form";
import { config } from "./form-config";
import { IFormItem } from "./form-item";
/**
* 表单弹窗组件
*/
export const FormModal = defineComponent({
name: "FormModal",
inheritAttrs: false,
props: {
/**
* 弹窗标题
* @default '添加'
*/
title: {
type: [String, Function] as PropType<
string | ((args: { model: Record<string, any>; items: IFormItem[] }) => string)
>,
default: "添加",
},
/**
* 触发元素
*/
trigger: {
type: [Boolean, Function, Object] as PropType<
| boolean
| ((props: { model: any; items: any[] }) => VNode)
| {
text?: string;
buttonProps?: ButtonInstance["$props"];
}
>,
default: true,
},
/**
* 传递给Modal组件的props
*/
modalProps: {
type: Object as PropType<Omit<InstanceType<typeof Modal>["$props"], "visible" | "title" | "onBeforeOk">>,
},
/**
* 表单数据
*/
model: {
type: Object as PropType<Record<string, any>>,
required: true,
},
/**
* 表单各项
*/
items: {
type: Array as PropType<IFormItem[]>,
required: true,
},
/**
* 提交表单的函数
* @description 可返回`{ message }`类型,用于显示提示信息
*/
submit: {
type: Function as PropType<(arg: { model: Record<string, any>; items: IFormItem[] }) => any | Promise<any>>,
default: () => true,
},
/**
* 传递给Form组件的props
*/
formProps: {
type: Object as PropType<Omit<FormInstance["$props"], "model">>,
},
},
emits: ["close", "submited"],
setup(props, { slots, emit, attrs }) {
const origin = cloneDeep(props.model);
const formRef = ref<InstanceType<typeof Form>>();
const loading = ref(false);
const visible = ref(false);
const open = async (data: Record<string, any> = {}) => {
visible.value = true;
await nextTick();
for (const key in data) {
props.model[key] = data[key];
}
};
const onBeforeOk = async () => {
if (typeof attrs.onBeforeOk === "function") {
const isOk = await attrs.onBeforeOk();
if (!isOk) return false;
}
const errors = await formRef.value?.formRef?.validate();
if (errors) {
return false;
}
try {
const model = formRef.value?.getModel() || {};
const res = await props.submit?.({ items: props.items, model });
res?.data?.message && Message.success(`提示: ${res.data.message}`);
emit("submited", res);
} catch (error: any) {
const message = config.getApiErrorMessage(error);
if (message) {
Message.error(`提示: ${message}`);
}
return false;
}
return true;
};
const onClose = () => {
visible.value = false;
assign(props.model, origin);
emit("close");
};
const modalTitle = computed(() => {
if (typeof props.title === "string") {
return props.title;
}
if (typeof props.title === "function") {
return props.title({ model: props.model, items: props.items });
}
});
const modalTrigger = computed(() => {
if (!props.trigger) {
return null;
}
let content;
if (typeof props.trigger === "boolean" || typeof props.trigger === "string") {
content = (
<Button type="primary">
{{
default: () => (typeof props.trigger === "string" ? props.trigger : "添加"),
icon: () => <i class="icon-park-outline-plus" />,
}}
</Button>
);
}
if (typeof props.trigger === "function") {
content = props.trigger({ model: props.model, items: props.items });
}
if (typeof props.trigger === "object") {
content = (
<Button type="primary" {...omit(props.trigger, "text")}>
{props.trigger?.text || "添加"}
</Button>
);
}
if (slots.trigger) {
content = slots.trigger({ model: props.model, items: props.items });
}
return <span onClick={() => open()}>{content}</span>;
});
return {
origin,
formRef,
loading,
visible,
modalTitle,
modalTrigger,
open,
onClose,
onBeforeOk,
};
},
render() {
return (
<>
{this.modalTrigger}
<Modal
{...this.modalProps}
v-model:visible={this.visible}
onBeforeOk={this.onBeforeOk}
onClose={this.onClose}
title={this.modalTitle}
>
{this.visible && (
<Form ref={(el: any) => (this.formRef = el)} {...this.formProps} model={this.model} items={this.items}>
{{ ...this.$slots }}
</Form>
)}
</Modal>
</>
);
},
});
export type FormModalInstance = InstanceType<typeof FormModal>;
export type FormModalProps = FormModalInstance["$props"];
export default FormModal;