198 lines
5.2 KiB
TypeScript
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;
|