Fixed the issue where variables were not displayed in the switch operator #3221 (#8601)

### What problem does this PR solve?

Feat: Fixed the issue where variables were not displayed in the switch
operator #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-07-01 15:52:14 +08:00
committed by GitHub
parent 1c77b4ed9b
commit 6b04b07eb4
7 changed files with 154 additions and 122 deletions

View File

@ -24,6 +24,7 @@ import {
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { cn } from '@/lib/utils';
import { RAGFlowSelectOptionType } from '../ui/select';
const countries = [
@ -77,96 +78,105 @@ export type SelectWithSearchFlagProps = {
options?: SelectWithSearchFlagOptionType[];
value?: string;
onChange?(value: string): void;
triggerClassName?: string;
};
export const SelectWithSearch = forwardRef<
React.ElementRef<typeof Button>,
SelectWithSearchFlagProps
>(({ value: val = '', onChange, options = countries }, ref) => {
const id = useId();
const [open, setOpen] = useState<boolean>(false);
const [value, setValue] = useState<string>('');
>(
(
{ value: val = '', onChange, options = countries, triggerClassName },
ref,
) => {
const id = useId();
const [open, setOpen] = useState<boolean>(false);
const [value, setValue] = useState<string>('');
const handleSelect = useCallback(
(val: string) => {
const handleSelect = useCallback(
(val: string) => {
setValue(val);
setOpen(false);
onChange?.(val);
},
[onChange],
);
useEffect(() => {
setValue(val);
setOpen(false);
onChange?.(val);
},
[onChange],
);
}, [val]);
useEffect(() => {
setValue(val);
}, [val]);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
id={id}
variant="outline"
role="combobox"
aria-expanded={open}
ref={ref}
className="bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]"
>
{value ? (
<span className="flex min-w-0 options-center gap-2">
<span className="text-lg leading-none truncate">
{
options
.map((group) =>
group.options.find((item) => item.value === value),
)
.filter(Boolean)[0]?.label
}
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
id={id}
variant="outline"
role="combobox"
aria-expanded={open}
ref={ref}
className={cn(
'bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px]',
triggerClassName,
)}
>
{value ? (
<span className="flex min-w-0 options-center gap-2">
<span className="text-lg leading-none truncate">
{
options
.map((group) =>
group.options.find((item) => item.value === value),
)
.filter(Boolean)[0]?.label
}
</span>
</span>
</span>
) : (
<span className="text-muted-foreground">Select value</span>
)}
<ChevronDownIcon
size={16}
className="text-muted-foreground/80 shrink-0"
aria-hidden="true"
/>
</Button>
</PopoverTrigger>
<PopoverContent
className="border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0"
align="start"
>
<Command>
<CommandInput placeholder="Search ..." />
<CommandList>
<CommandEmpty>No data found.</CommandEmpty>
{options.map((group) => (
<Fragment key={group.label}>
<CommandGroup heading={group.label}>
{group.options.map((option) => (
<CommandItem
key={option.value}
value={option.value}
onSelect={handleSelect}
>
<span className="text-lg leading-none">
{option.label}
</span>
) : (
<span className="text-muted-foreground">Select value</span>
)}
<ChevronDownIcon
size={16}
className="text-muted-foreground/80 shrink-0"
aria-hidden="true"
/>
</Button>
</PopoverTrigger>
<PopoverContent
className="border-input w-full min-w-[var(--radix-popper-anchor-width)] p-0"
align="start"
>
<Command>
<CommandInput placeholder="Search ..." />
<CommandList>
<CommandEmpty>No data found.</CommandEmpty>
{options.map((group) => (
<Fragment key={group.label}>
<CommandGroup heading={group.label}>
{group.options.map((option) => (
<CommandItem
key={option.value}
value={option.value}
onSelect={handleSelect}
>
<span className="text-lg leading-none">
{option.label}
</span>
{value === option.value && (
<CheckIcon size={16} className="ml-auto" />
)}
</CommandItem>
))}
</CommandGroup>
</Fragment>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
});
{value === option.value && (
<CheckIcon size={16} className="ml-auto" />
)}
</CommandItem>
))}
</CommandGroup>
</Fragment>
))}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
},
);
SelectWithSearch.displayName = 'SelectWithSearch';

View File

@ -4,7 +4,7 @@ import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow';
import { NodeProps, Position } from '@xyflow/react';
import { memo, useCallback } from 'react';
import { NodeHandleId, SwitchOperatorOptions } from '../../constant';
import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query';
import { useGetVariableLabelByValue } from '../../hooks/use-get-begin-query';
import { CommonHandle } from './handle';
import { RightHandleStyle } from './handle-icon';
import NodeHeader from './node-header';
@ -25,12 +25,9 @@ const getConditionKey = (idx: number, length: number) => {
const ConditionBlock = ({
condition,
nodeId,
}: {
condition: ISwitchCondition;
nodeId: string;
}) => {
}: { condition: ISwitchCondition } & { nodeId: string }) => {
const items = condition?.items ?? [];
const getLabel = useGetComponentLabelByValue(nodeId);
const getLabel = useGetVariableLabelByValue(nodeId);
const renderOperatorIcon = useCallback((operator?: string) => {
const name = SwitchOperatorOptions.find((x) => x.value === operator)?.icon;
@ -83,8 +80,8 @@ function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) {
</div>
{position.condition && (
<ConditionBlock
nodeId={id}
condition={position.condition}
nodeId={id}
></ConditionBlock>
)}
</section>

View File

@ -81,7 +81,7 @@ export enum Operator {
Email = 'Email',
Iteration = 'Iteration',
IterationStart = 'IterationItem',
Code = 'Code',
Code = 'CodeExec',
WaitingDialogue = 'WaitingDialogue',
Agent = 'Agent',
Tool = 'Tool',

View File

@ -1,5 +1,6 @@
import { FormContainer } from '@/components/form-container';
import { IconFont } from '@/components/icon-font';
import { SelectWithSearch } from '@/components/originui/select-with-search';
import { BlockButton, Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import {
@ -12,21 +13,20 @@ import {
import { RAGFlowSelect } from '@/components/ui/select';
import { Separator } from '@/components/ui/separator';
import { Textarea } from '@/components/ui/textarea';
import { ISwitchForm } from '@/interfaces/database/agent';
import { cn } from '@/lib/utils';
import { zodResolver } from '@hookform/resolvers/zod';
import { toLower } from 'lodash';
import { X } from 'lucide-react';
import { useCallback, useMemo } from 'react';
import { useFieldArray, useForm, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { z } from 'zod';
import {
Operator,
SwitchLogicOperatorOptions,
SwitchOperatorOptions,
VariableType,
} from '../../constant';
import { useBuildFormSelectOptions } from '../../form-hooks';
import { useBuildComponentIdAndBeginOptions } from '../../hooks/use-get-begin-query';
import { useBuildQueryVariableOptions } from '../../hooks/use-get-begin-query';
import { IOperatorForm } from '../../interface';
import { useValues } from './use-values';
import { useWatchFormChange } from './use-watch-change';
@ -71,18 +71,24 @@ function useBuildSwitchOperatorOptions() {
function ConditionCards({
name: parentName,
node,
parentIndex,
removeParent,
parentLength,
}: ConditionCardsProps) {
const form = useFormContext();
const { t } = useTranslation();
const componentIdOptions = useBuildComponentIdAndBeginOptions(
node?.id,
node?.parentId,
);
const nextOptions = useBuildQueryVariableOptions();
const finalOptions = useMemo(() => {
return nextOptions.map((x) => {
return {
...x,
options: x.options.filter(
(y) => !toLower(y.type).includes(VariableType.Array),
),
};
});
}, [nextOptions]);
const switchOperatorOptions = useBuildSwitchOperatorOptions();
@ -124,12 +130,11 @@ function ConditionCards({
render={({ field }) => (
<FormItem>
<FormControl>
<RAGFlowSelect
<SelectWithSearch
{...field}
options={componentIdOptions}
placeholder={t('common.pleaseSelect')}
triggerClassName="w-30 text-background-checked bg-transparent border-none"
/>
options={finalOptions}
triggerClassName="w-30 text-background-checked bg-transparent border-none text-ellipsis"
></SelectWithSearch>
</FormControl>
<FormMessage />
</FormItem>
@ -224,17 +229,6 @@ const SwitchForm = ({ node }: IOperatorForm) => {
control: form.control,
});
const buildCategorizeToOptions = useBuildFormSelectOptions(
Operator.Switch,
node?.id,
);
const getSelectedConditionTos = () => {
const conditions: ISwitchForm['conditions'] = form?.getValues('conditions');
return conditions?.filter((x) => !!x).map((x) => x?.to) ?? [];
};
const switchLogicOperatorOptions = useMemo(() => {
return SwitchLogicOperatorOptions.map((x) => ({
value: x,

View File

@ -9,8 +9,9 @@ export function useWatchFormChange(id?: string, form?: UseFormReturn) {
useEffect(() => {
// Manually triggered form updates are synchronized to the canvas
if (id && form?.formState.isDirty) {
values = form?.getValues();
console.log('🚀 ~ useWatchFormChange ~ values:', form?.formState.isDirty);
if (id) {
values = form?.getValues() || {};
let nextValues: any = {
...values,
conditions:

View File

@ -2,7 +2,6 @@ import { Operator } from '../../constant';
import AkShareForm from '../akshare-form';
import ArXivForm from '../arxiv-form';
import BingForm from '../bing-form';
import CodeForm from '../code-form';
import CrawlerForm from '../crawler-form';
import DeepLForm from '../deepl-form';
import DuckDuckGoForm from '../duckduckgo-form';
@ -19,7 +18,7 @@ import TavilyForm from './tavily-form';
export const ToolFormConfigMap = {
[Operator.Retrieval]: RetrievalForm,
[Operator.Code]: CodeForm,
[Operator.Code]: () => <div></div>,
[Operator.DuckDuckGo]: DuckDuckGoForm,
[Operator.Wikipedia]: WikipediaForm,
[Operator.PubMed]: PubMedForm,

View File

@ -24,6 +24,18 @@ export const useGetBeginNodeDataQuery = () => {
return getBeginNodeDataQuery;
};
export const useGetBeginNodeDataInputs = () => {
const getNode = useGraphStore((state) => state.getNode);
const inputs = get(getNode(BeginId), 'data.form.inputs', {});
const beginNodeDataInputs = useMemo(() => {
return buildBeginInputListFromObject(inputs);
}, [inputs]);
return beginNodeDataInputs;
};
export const useGetBeginNodeDataQueryIsSafe = () => {
const [isBeginNodeDataQuerySafe, setIsBeginNodeDataQuerySafe] =
useState(false);
@ -152,9 +164,9 @@ export const useBuildVariableOptions = (nodeId?: string, parentId?: string) => {
return options;
};
export function useBuildQueryVariableOptions() {
export function useBuildQueryVariableOptions(n?: RAGFlowNodeType) {
const { data } = useFetchAgent();
const node = useContext(AgentFormContext);
const node = useContext(AgentFormContext) || n;
const options = useBuildVariableOptions(node?.id, node?.parentId);
const nextOptions = useMemo(() => {
@ -170,7 +182,7 @@ export function useBuildQueryVariableOptions() {
{ ...options[0], options: [...options[0]?.options, ...globalOptions] },
...options.slice(1),
];
}, [data.dsl.globals, options]);
}, [data.dsl?.globals, options]);
return nextOptions;
}
@ -241,3 +253,22 @@ export const useGetComponentLabelByValue = (nodeId: string) => {
);
return getLabel;
};
export function useGetVariableLabelByValue(nodeId: string) {
const { getNode } = useGraphStore((state) => state);
const nextOptions = useBuildQueryVariableOptions(getNode(nodeId));
const flattenOptions = useMemo(() => {
return nextOptions.reduce<DefaultOptionType[]>((pre, cur) => {
return [...pre, ...cur.options];
}, []);
}, [nextOptions]);
const getLabel = useCallback(
(val?: string) => {
return flattenOptions.find((x) => x.value === val)?.label;
},
[flattenOptions],
);
return getLabel;
}