From 6548b16b6cee53538c5da606b641e2b5cd53ea15 Mon Sep 17 00:00:00 2001 From: bill Date: Tue, 25 Nov 2025 09:55:17 +0800 Subject: [PATCH] Feat: Synchronize variable data to the outputs field --- web/src/pages/agent/form/loop-form/index.tsx | 31 +++-------------- web/src/pages/agent/form/loop-form/schema.ts | 24 ++++++++++++++ .../pages/agent/form/loop-form/use-values.ts | 20 +++++++++++ .../form/loop-form/use-watch-form-change.ts | 33 +++++++++++++++++++ web/src/pages/agent/interface.ts | 8 +++++ 5 files changed, 90 insertions(+), 26 deletions(-) create mode 100644 web/src/pages/agent/form/loop-form/schema.ts create mode 100644 web/src/pages/agent/form/loop-form/use-values.ts create mode 100644 web/src/pages/agent/form/loop-form/use-watch-form-change.ts diff --git a/web/src/pages/agent/form/loop-form/index.tsx b/web/src/pages/agent/form/loop-form/index.tsx index a50242565..69eb3ac2f 100644 --- a/web/src/pages/agent/form/loop-form/index.tsx +++ b/web/src/pages/agent/form/loop-form/index.tsx @@ -4,45 +4,24 @@ import { FormLayout } from '@/constants/form'; import { zodResolver } from '@hookform/resolvers/zod'; import { memo } from 'react'; import { useForm } from 'react-hook-form'; -import { z } from 'zod'; import { initialLoopValues } from '../../constant'; -import { useFormValues } from '../../hooks/use-form-values'; -import { useWatchFormChange } from '../../hooks/use-watch-form-change'; import { INextOperatorForm } from '../../interface'; import { FormWrapper } from '../components/form-wrapper'; import { DynamicVariables } from './dynamic-variables'; import { LoopTerminationCondition } from './loop-termination-condition'; - -const FormSchema = z.object({ - loop_variables: z.array( - z.object({ - variable: z.string().optional(), - type: z.string().optional(), - value: z.string().or(z.number()).or(z.boolean()).optional(), - input_mode: z.string(), - }), - ), - logical_operator: z.string(), - loop_termination_condition: z.array( - z.object({ - variable: z.string().optional(), - operator: z.string().optional(), - value: z.string().or(z.number()).or(z.boolean()).optional(), - input_mode: z.string().optional(), - }), - ), - maximum_loop_count: z.number(), -}); +import { FormSchema, LoopFormSchemaType } from './schema'; +import { useFormValues } from './use-values'; +import { useWatchFormChange } from './use-watch-form-change'; function LoopForm({ node }: INextOperatorForm) { const defaultValues = useFormValues(initialLoopValues, node); - const form = useForm({ + const form = useForm({ defaultValues: defaultValues, resolver: zodResolver(FormSchema), }); - useWatchFormChange(node?.id, form, true); + useWatchFormChange(node?.id, form); return (
diff --git a/web/src/pages/agent/form/loop-form/schema.ts b/web/src/pages/agent/form/loop-form/schema.ts new file mode 100644 index 000000000..982cbb305 --- /dev/null +++ b/web/src/pages/agent/form/loop-form/schema.ts @@ -0,0 +1,24 @@ +import { z } from 'zod'; + +export const FormSchema = z.object({ + loop_variables: z.array( + z.object({ + variable: z.string().optional(), + type: z.string().optional(), + value: z.string().or(z.number()).or(z.boolean()).optional(), + input_mode: z.string(), + }), + ), + logical_operator: z.string(), + loop_termination_condition: z.array( + z.object({ + variable: z.string().optional(), + operator: z.string().optional(), + value: z.string().or(z.number()).or(z.boolean()).optional(), + input_mode: z.string().optional(), + }), + ), + maximum_loop_count: z.number(), +}); + +export type LoopFormSchemaType = z.infer; diff --git a/web/src/pages/agent/form/loop-form/use-values.ts b/web/src/pages/agent/form/loop-form/use-values.ts new file mode 100644 index 000000000..cf7a1054a --- /dev/null +++ b/web/src/pages/agent/form/loop-form/use-values.ts @@ -0,0 +1,20 @@ +import { RAGFlowNodeType } from '@/interfaces/database/flow'; +import { isEmpty, omit } from 'lodash'; +import { useMemo } from 'react'; + +export function useFormValues( + defaultValues: Record, + node?: RAGFlowNodeType, +) { + const values = useMemo(() => { + const formData = node?.data?.form; + + if (isEmpty(formData)) { + return omit(defaultValues, 'outputs'); + } + + return omit(formData, 'outputs'); + }, [defaultValues, node?.data?.form]); + + return values; +} diff --git a/web/src/pages/agent/form/loop-form/use-watch-form-change.ts b/web/src/pages/agent/form/loop-form/use-watch-form-change.ts new file mode 100644 index 000000000..ee2416d1f --- /dev/null +++ b/web/src/pages/agent/form/loop-form/use-watch-form-change.ts @@ -0,0 +1,33 @@ +import { useEffect } from 'react'; +import { UseFormReturn, useWatch } from 'react-hook-form'; +import { IOutputs } from '../../interface'; +import useGraphStore from '../../store'; +import { LoopFormSchemaType } from './schema'; + +export function useWatchFormChange( + id?: string, + form?: UseFormReturn, +) { + let values = useWatch({ control: form?.control }); + const { replaceNodeForm } = useGraphStore((state) => state); + + useEffect(() => { + if (id) { + let nextValues = { + ...values, + outputs: values.loop_variables?.reduce((pre, cur) => { + const variable = cur.variable; + if (variable) { + pre[variable] = { + type: 'string', + value: '', + }; + } + return pre; + }, {} as IOutputs), + }; + + replaceNodeForm(id, nextValues); + } + }, [form?.formState.isDirty, id, replaceNodeForm, values]); +} diff --git a/web/src/pages/agent/interface.ts b/web/src/pages/agent/interface.ts index 0a6cba2f0..ed823535b 100644 --- a/web/src/pages/agent/interface.ts +++ b/web/src/pages/agent/interface.ts @@ -41,3 +41,11 @@ export type IInputs = { prologue: string; mode: string; }; + +export type IOutputs = Record< + string, + { + type?: string; + value?: string; + } +>;