mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-24 21:36:06 +00:00
feat: switch dynamic to shadcn
This commit is contained in:
@@ -1,21 +1,139 @@
|
||||
import { IDynamicFormItemConfig } from '@/app/home/components/dynamic-form/DynamicFormItemConfig';
|
||||
import { Form, FormInstance } from 'antd';
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import DynamicFormItemComponent from '@/app/home/components/dynamic-form/DynamicFormItemComponent';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export default function DynamicFormComponent({
|
||||
form,
|
||||
itemConfigList,
|
||||
onSubmit,
|
||||
initialValues,
|
||||
}: {
|
||||
form: FormInstance<object>;
|
||||
itemConfigList: IDynamicFormItemConfig[];
|
||||
onSubmit?: (val: object) => unknown;
|
||||
initialValues?: Record<string, any>;
|
||||
}) {
|
||||
// 根据 itemConfigList 动态生成 zod schema
|
||||
const formSchema = z.object(
|
||||
itemConfigList.reduce((acc, item) => {
|
||||
let fieldSchema;
|
||||
switch (item.type) {
|
||||
case 'integer':
|
||||
fieldSchema = z.number();
|
||||
break;
|
||||
case 'float':
|
||||
fieldSchema = z.number();
|
||||
break;
|
||||
case 'boolean':
|
||||
fieldSchema = z.boolean();
|
||||
break;
|
||||
case 'string':
|
||||
fieldSchema = z.string();
|
||||
break;
|
||||
case 'array[string]':
|
||||
fieldSchema = z.array(z.string());
|
||||
break;
|
||||
case 'select':
|
||||
fieldSchema = z.string();
|
||||
break;
|
||||
default:
|
||||
fieldSchema = z.string();
|
||||
}
|
||||
|
||||
if (item.required && (fieldSchema instanceof z.ZodString || fieldSchema instanceof z.ZodArray)) {
|
||||
fieldSchema = fieldSchema.min(1, { message: '此字段为必填项' });
|
||||
}
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[item.name]: fieldSchema,
|
||||
};
|
||||
}, {} as Record<string, z.ZodTypeAny>)
|
||||
);
|
||||
|
||||
type FormValues = z.infer<typeof formSchema>;
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: itemConfigList.reduce((acc, item) => {
|
||||
// 优先使用 initialValues,如果没有则使用默认值
|
||||
const value = initialValues?.[item.name] ?? item.default;
|
||||
return {
|
||||
...acc,
|
||||
[item.name]: value,
|
||||
};
|
||||
}, {} as FormValues),
|
||||
});
|
||||
|
||||
// 当 initialValues 变化时更新表单值
|
||||
useEffect(() => {
|
||||
console.log('initialValues', initialValues);
|
||||
if (initialValues) {
|
||||
// 合并默认值和初始值
|
||||
const mergedValues = itemConfigList.reduce((acc, item) => {
|
||||
acc[item.name] = initialValues[item.name] ?? item.default;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
|
||||
Object.entries(mergedValues).forEach(([key, value]) => {
|
||||
form.setValue(key as keyof FormValues, value);
|
||||
});
|
||||
}
|
||||
}, [initialValues, form, itemConfigList]);
|
||||
|
||||
// 监听表单值变化
|
||||
useEffect(() => {
|
||||
const subscription = form.watch((value) => {
|
||||
// 获取完整的表单值,确保包含所有默认值
|
||||
const formValues = form.getValues();
|
||||
console.log('formValues', formValues);
|
||||
const finalValues = itemConfigList.reduce((acc, item) => {
|
||||
acc[item.name] = formValues[item.name] ?? item.default;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
console.log('finalValues', finalValues);
|
||||
onSubmit?.(finalValues);
|
||||
});
|
||||
return () => subscription.unsubscribe();
|
||||
}, [form, onSubmit, itemConfigList]);
|
||||
|
||||
return (
|
||||
<Form form={form} onFinish={onSubmit} layout={'vertical'}>
|
||||
{itemConfigList.map((config) => (
|
||||
<DynamicFormItemComponent key={config.id} config={config} />
|
||||
))}
|
||||
<Form {...form}>
|
||||
<div className="space-y-4">
|
||||
{itemConfigList.map((config) => (
|
||||
<FormField
|
||||
key={config.id}
|
||||
control={form.control}
|
||||
name={config.name as keyof FormValues}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{config.label.zh_CN} {config.required && <span className="text-red-500">*</span>}</FormLabel>
|
||||
<FormControl>
|
||||
<DynamicFormItemComponent
|
||||
config={config}
|
||||
field={field}
|
||||
/>
|
||||
</FormControl>
|
||||
{config.description && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{config.description.zh_CN}
|
||||
</p>
|
||||
)}
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user