mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-07-24 21:36:06 +00:00
fix: lint code to build success
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { IDynamicFormItemSchema } from '@/app/infra/entities/form/dynamic';
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
} from '@/components/ui/form';
|
||||
import DynamicFormItemComponent from '@/app/home/components/dynamic-form/DynamicFormItemComponent';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
@@ -20,53 +20,62 @@ export default function DynamicFormComponent({
|
||||
}: {
|
||||
itemConfigList: IDynamicFormItemSchema[];
|
||||
onSubmit?: (val: object) => unknown;
|
||||
initialValues?: Record<string, any>;
|
||||
initialValues?: Record<string, object>;
|
||||
}) {
|
||||
// 根据 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;
|
||||
case 'llm-model-selector':
|
||||
fieldSchema = z.string();
|
||||
break;
|
||||
case 'prompt-editor':
|
||||
fieldSchema = z.array(z.object({
|
||||
content: z.string(),
|
||||
role: z.string(),
|
||||
}));
|
||||
break;
|
||||
default:
|
||||
fieldSchema = z.string();
|
||||
}
|
||||
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;
|
||||
case 'llm-model-selector':
|
||||
fieldSchema = z.string();
|
||||
break;
|
||||
case 'prompt-editor':
|
||||
fieldSchema = z.array(
|
||||
z.object({
|
||||
content: z.string(),
|
||||
role: z.string(),
|
||||
}),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
fieldSchema = z.string();
|
||||
}
|
||||
|
||||
if (item.required && (fieldSchema instanceof z.ZodString || fieldSchema instanceof z.ZodArray)) {
|
||||
fieldSchema = fieldSchema.min(1, { message: '此字段为必填项' });
|
||||
}
|
||||
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>)
|
||||
return {
|
||||
...acc,
|
||||
[item.name]: fieldSchema,
|
||||
};
|
||||
},
|
||||
{} as Record<string, z.ZodTypeAny>,
|
||||
),
|
||||
);
|
||||
|
||||
type FormValues = z.infer<typeof formSchema>;
|
||||
@@ -88,11 +97,14 @@ export default function DynamicFormComponent({
|
||||
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>);
|
||||
|
||||
const mergedValues = itemConfigList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = initialValues[item.name] ?? item.default;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, object>,
|
||||
);
|
||||
|
||||
Object.entries(mergedValues).forEach(([key, value]) => {
|
||||
form.setValue(key as keyof FormValues, value);
|
||||
});
|
||||
@@ -101,14 +113,17 @@ export default function DynamicFormComponent({
|
||||
|
||||
// 监听表单值变化
|
||||
useEffect(() => {
|
||||
const subscription = form.watch((value) => {
|
||||
const subscription = form.watch(() => {
|
||||
// 获取完整的表单值,确保包含所有默认值
|
||||
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>);
|
||||
const finalValues = itemConfigList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = formValues[item.name] ?? item.default;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, object>,
|
||||
);
|
||||
console.log('finalValues', finalValues);
|
||||
onSubmit?.(finalValues);
|
||||
});
|
||||
@@ -125,12 +140,12 @@ export default function DynamicFormComponent({
|
||||
name={config.name as keyof FormValues}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{config.label.zh_CN} {config.required && <span className="text-red-500">*</span>}</FormLabel>
|
||||
<FormLabel>
|
||||
{config.label.zh_CN}{' '}
|
||||
{config.required && <span className="text-red-500">*</span>}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<DynamicFormItemComponent
|
||||
config={config}
|
||||
field={field}
|
||||
/>
|
||||
<DynamicFormItemComponent config={config} field={field} />
|
||||
</FormControl>
|
||||
{config.description && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
||||
@@ -2,32 +2,43 @@ import {
|
||||
DynamicFormItemType,
|
||||
IDynamicFormItemSchema,
|
||||
} from '@/app/infra/entities/form/dynamic';
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { ControllerRenderProps } from "react-hook-form";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useEffect, useState } from "react";
|
||||
import { httpClient } from "@/app/infra/http/HttpClient";
|
||||
import { LLMModel } from "@/app/infra/entities/api";
|
||||
import { toast } from "sonner";
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { ControllerRenderProps } from 'react-hook-form';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { LLMModel } from '@/app/infra/entities/api';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export default function DynamicFormItemComponent({
|
||||
config,
|
||||
field,
|
||||
}: {
|
||||
config: IDynamicFormItemSchema;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
field: ControllerRenderProps<any, any>;
|
||||
}) {
|
||||
const [llmModels, setLlmModels] = useState<LLMModel[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (config.type === DynamicFormItemType.LLM_MODEL_SELECTOR) {
|
||||
httpClient.getProviderLLMModels().then((resp) => {
|
||||
setLlmModels(resp.models);
|
||||
}).catch((err) => {
|
||||
toast.error("获取 LLM 模型列表失败:" + err.message);
|
||||
});
|
||||
httpClient
|
||||
.getProviderLLMModels()
|
||||
.then((resp) => {
|
||||
setLlmModels(resp.models);
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('获取 LLM 模型列表失败:' + err.message);
|
||||
});
|
||||
}
|
||||
}, [config.type]);
|
||||
|
||||
@@ -46,12 +57,7 @@ export default function DynamicFormItemComponent({
|
||||
return <Input {...field} />;
|
||||
|
||||
case DynamicFormItemType.BOOLEAN:
|
||||
return (
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
);
|
||||
return <Switch checked={field.value} onCheckedChange={field.onChange} />;
|
||||
|
||||
case DynamicFormItemType.STRING_ARRAY:
|
||||
return (
|
||||
@@ -67,15 +73,22 @@ export default function DynamicFormItemComponent({
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
<button
|
||||
type="button"
|
||||
className="p-2 hover:bg-gray-100 rounded"
|
||||
onClick={() => {
|
||||
const newValue = field.value.filter((_: string, i: number) => i !== index);
|
||||
const newValue = field.value.filter(
|
||||
(_: string, i: number) => i !== index,
|
||||
);
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5 text-red-500">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="w-5 h-5 text-red-500"
|
||||
>
|
||||
<path d="M7 4V2H17V4H22V6H20V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V6H2V4H7ZM6 6V20H18V6H6ZM9 9H11V17H9V9ZM13 9H15V17H13V9Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
@@ -95,10 +108,7 @@ export default function DynamicFormItemComponent({
|
||||
|
||||
case DynamicFormItemType.SELECT:
|
||||
return (
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
@@ -116,10 +126,7 @@ export default function DynamicFormItemComponent({
|
||||
|
||||
case DynamicFormItemType.LLM_MODEL_SELECTOR:
|
||||
return (
|
||||
<Select
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择模型" />
|
||||
</SelectTrigger>
|
||||
@@ -138,66 +145,78 @@ export default function DynamicFormItemComponent({
|
||||
case DynamicFormItemType.PROMPT_EDITOR:
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{field.value.map((item: { role: string; content: string }, index: number) => (
|
||||
<div key={index} className="flex gap-2 items-center">
|
||||
{/* 角色选择 */}
|
||||
{index === 0 ? (
|
||||
<div className="w-[120px] px-3 py-2 border rounded bg-gray-50 text-gray-500">system</div>
|
||||
) : (
|
||||
<Select
|
||||
value={item.role}
|
||||
onValueChange={(value) => {
|
||||
{field.value.map(
|
||||
(item: { role: string; content: string }, index: number) => (
|
||||
<div key={index} className="flex gap-2 items-center">
|
||||
{/* 角色选择 */}
|
||||
{index === 0 ? (
|
||||
<div className="w-[120px] px-3 py-2 border rounded bg-gray-50 text-gray-500">
|
||||
system
|
||||
</div>
|
||||
) : (
|
||||
<Select
|
||||
value={item.role}
|
||||
onValueChange={(value) => {
|
||||
const newValue = [...field.value];
|
||||
newValue[index] = { ...newValue[index], role: value };
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="user">user</SelectItem>
|
||||
<SelectItem value="assistant">assistant</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
{/* 内容输入 */}
|
||||
<Input
|
||||
className="w-[300px]"
|
||||
value={item.content}
|
||||
onChange={(e) => {
|
||||
const newValue = [...field.value];
|
||||
newValue[index] = { ...newValue[index], role: value };
|
||||
newValue[index] = {
|
||||
...newValue[index],
|
||||
content: e.target.value,
|
||||
};
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="user">user</SelectItem>
|
||||
<SelectItem value="assistant">assistant</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
{/* 内容输入 */}
|
||||
<Input
|
||||
className="w-[300px]"
|
||||
value={item.content}
|
||||
onChange={(e) => {
|
||||
const newValue = [...field.value];
|
||||
newValue[index] = { ...newValue[index], content: e.target.value };
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
/>
|
||||
{/* 删除按钮,第一轮不显示 */}
|
||||
{index !== 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className="p-2 hover:bg-gray-100 rounded"
|
||||
onClick={() => {
|
||||
const newValue = field.value.filter((_: any, i: number) => i !== index);
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-5 h-5 text-red-500">
|
||||
<path d="M7 4V2H17V4H22V6H20V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V6H2V4H7ZM6 6V20H18V6H6ZM9 9H11V17H9V9ZM13 9H15V17H13V9Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
/>
|
||||
{/* 删除按钮,第一轮不显示 */}
|
||||
{index !== 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className="p-2 hover:bg-gray-100 rounded"
|
||||
onClick={() => {
|
||||
const newValue = field.value.filter(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(_: any, i: number) => i !== index,
|
||||
);
|
||||
field.onChange(newValue);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
className="w-5 h-5 text-red-500"
|
||||
>
|
||||
<path d="M7 4V2H17V4H22V6H20V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V6H2V4H7ZM6 6V20H18V6H6ZM9 9H11V17H9V9ZM13 9H15V17H13V9Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
field.onChange([
|
||||
...field.value,
|
||||
{ role: 'user', content: '' },
|
||||
]);
|
||||
field.onChange([...field.value, { role: 'user', content: '' }]);
|
||||
}}
|
||||
>
|
||||
添加回合
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { IDynamicFormItemSchema, DynamicFormItemType, IDynamicFormItemOption } from '@/app/infra/entities/form/dynamic';
|
||||
import {
|
||||
IDynamicFormItemSchema,
|
||||
DynamicFormItemType,
|
||||
IDynamicFormItemOption,
|
||||
} from '@/app/infra/entities/form/dynamic';
|
||||
import { I18nLabel } from '@/app/infra/entities/common';
|
||||
|
||||
export class DynamicFormItemConfig implements IDynamicFormItemSchema {
|
||||
@@ -23,7 +27,6 @@ export class DynamicFormItemConfig implements IDynamicFormItemSchema {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function isDynamicFormItemType(
|
||||
value: string,
|
||||
): value is DynamicFormItemType {
|
||||
@@ -36,9 +39,16 @@ export function parseDynamicFormItemType(value: string): DynamicFormItemType {
|
||||
return isDynamicFormItemType(value) ? value : DynamicFormItemType.UNKNOWN;
|
||||
}
|
||||
|
||||
export function getDefaultValues(itemConfigList: IDynamicFormItemSchema[]): Record<string, any> {
|
||||
return itemConfigList.reduce((acc, item) => {
|
||||
acc[item.name] = item.default;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
export function getDefaultValues(
|
||||
itemConfigList: IDynamicFormItemSchema[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
): Record<string, any> {
|
||||
return itemConfigList.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.name] = item.default;
|
||||
return acc;
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
{} as Record<string, any>,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user