120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
import { Form as BaseForm, FormInstance as BaseFormInstance, Message } from "@arco-design/web-vue";
|
|
import { assign, cloneDeep, defaultsDeep } from "lodash-es";
|
|
import { PropType } from "vue";
|
|
import { config } from "./form-config";
|
|
import { FormItem, IFormItem } from "./form-item";
|
|
import { NodeType, nodeMap } from "./form-node";
|
|
|
|
type SubmitFn = (arg: { model: Record<string, any>; items: IFormItem[] }) => Promise<any>;
|
|
|
|
/**
|
|
* 表单组件
|
|
*/
|
|
export const Form = defineComponent({
|
|
name: "Form",
|
|
props: {
|
|
/**
|
|
* 表单数据
|
|
*/
|
|
model: {
|
|
type: Object as PropType<Record<any, any>>,
|
|
default: () => reactive({}),
|
|
},
|
|
/**
|
|
* 表单项
|
|
*/
|
|
items: {
|
|
type: Array as PropType<IFormItem[]>,
|
|
default: () => [],
|
|
},
|
|
/**
|
|
* 提交表单
|
|
*/
|
|
submit: {
|
|
type: Function as PropType<SubmitFn>,
|
|
},
|
|
/**
|
|
* 传给Form组件的参数
|
|
*/
|
|
formProps: {
|
|
type: Object as PropType<Omit<BaseFormInstance["$props"], "model">>,
|
|
},
|
|
},
|
|
setup(props) {
|
|
const model = cloneDeep(props.model);
|
|
const formRef = ref<InstanceType<typeof BaseForm>>();
|
|
const loading = ref(false);
|
|
|
|
props.items.forEach((item: any) => {
|
|
const node = nodeMap[item.type as NodeType];
|
|
defaultsDeep(item, { nodeProps: node?.nodeProps ?? {} });
|
|
(node as any)?.init?.({ item, model: props.model });
|
|
});
|
|
|
|
const getItem = (field: string) => {
|
|
return props.items.find((item) => item.field === field);
|
|
};
|
|
|
|
const getModel = () => {
|
|
return config.getModel(props.model);
|
|
};
|
|
|
|
const setModel = (data: Record<string, any>) => {
|
|
config.setModel(props.model, data);
|
|
};
|
|
|
|
const resetModel = () => {
|
|
assign(props.model, model);
|
|
};
|
|
|
|
const submitForm = async () => {
|
|
if (await formRef.value?.validate()) {
|
|
return;
|
|
}
|
|
const model: Record<string, any> = getModel();
|
|
try {
|
|
loading.value = true;
|
|
const res = await props.submit?.({ model, items: props.items });
|
|
res?.message && Message.success(`提示: ${res.message}`);
|
|
} catch (error: any) {
|
|
const message = error?.response?.data?.message || error?.message;
|
|
message && Message.error(`提示: ${message}`);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
return {
|
|
formRef,
|
|
loading,
|
|
getItem,
|
|
submitForm,
|
|
resetModel,
|
|
setModel,
|
|
getModel,
|
|
};
|
|
},
|
|
render() {
|
|
(this.items as any).instance = this;
|
|
|
|
const props = {
|
|
items: this.items,
|
|
model: this.model,
|
|
slots: this.$slots,
|
|
};
|
|
|
|
return (
|
|
<BaseForm ref="formRef" layout="vertical" model={this.model} {...this.$attrs} {...this.formProps}>
|
|
{this.items.map((item) => (
|
|
<FormItem loading={this.loading} onSubmit={this.submitForm} item={item} {...props}></FormItem>
|
|
))}
|
|
</BaseForm>
|
|
);
|
|
},
|
|
});
|
|
|
|
export type FormInstance = InstanceType<typeof Form>;
|
|
|
|
export type FormProps = FormInstance["$props"];
|
|
|
|
export type FormDefinedProps = Pick<FormProps, "model" | "items" | "submit" | "formProps">; |