diff --git a/web/src/pages/flow/canvas/node/categorize-node.tsx b/web/src/pages/flow/canvas/node/categorize-node.tsx index 1d5ecdd70..4b3240563 100644 --- a/web/src/pages/flow/canvas/node/categorize-node.tsx +++ b/web/src/pages/flow/canvas/node/categorize-node.tsx @@ -1,66 +1,21 @@ import { useTranslate } from '@/hooks/common-hooks'; import { Flex } from 'antd'; import classNames from 'classnames'; -import { pick } from 'lodash'; -import get from 'lodash/get'; -import intersectionWith from 'lodash/intersectionWith'; -import isEqual from 'lodash/isEqual'; import lowerFirst from 'lodash/lowerFirst'; -import { useEffect, useMemo, useState } from 'react'; -import { Handle, NodeProps, Position, useUpdateNodeInternals } from 'reactflow'; +import { Handle, NodeProps, Position } from 'reactflow'; import { Operator, operatorMap } from '../../constant'; -import { IPosition, NodeData } from '../../interface'; +import { NodeData } from '../../interface'; import OperatorIcon from '../../operator-icon'; -import { buildNewPositionMap } from '../../utils'; import CategorizeHandle from './categorize-handle'; import NodeDropdown from './dropdown'; +import { useBuildCategorizeHandlePositions } from './hooks'; import styles from './index.less'; import NodePopover from './popover'; export function CategorizeNode({ id, data, selected }: NodeProps) { - const updateNodeInternals = useUpdateNodeInternals(); - const [postionMap, setPositionMap] = useState>({}); - const categoryData = useMemo( - () => get(data, 'form.category_description') ?? {}, - [data], - ); const style = operatorMap[data.label as Operator]; const { t } = useTranslate('flow'); - - useEffect(() => { - // Cache used coordinates - setPositionMap((state) => { - // index in use - const indexesInUse = Object.values(state).map((x) => x.idx); - const categoryDataKeys = Object.keys(categoryData); - const stateKeys = Object.keys(state); - if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) { - const intersectionKeys = intersectionWith( - stateKeys, - categoryDataKeys, - (categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey, - ); - const newPositionMap = buildNewPositionMap( - categoryDataKeys.filter( - (x) => !intersectionKeys.some((y) => y === x), - ), - indexesInUse, - ); - - const nextPostionMap = { - ...pick(state, intersectionKeys), - ...newPositionMap, - }; - - return nextPostionMap; - } - return state; - }); - }, [categoryData]); - - useEffect(() => { - updateNodeInternals(id); - }, [id, updateNodeInternals, postionMap]); + const { positions } = useBuildCategorizeHandlePositions({ data, id }); return ( @@ -94,18 +49,15 @@ export function CategorizeNode({ id, data, selected }: NodeProps) { className={styles.handle} id={'c'} > - {Object.keys(categoryData).map((x, idx) => { - const position = postionMap[x]; + {positions.map((position, idx) => { return ( - position && ( - - ) + ); })} diff --git a/web/src/pages/flow/canvas/node/hooks.ts b/web/src/pages/flow/canvas/node/hooks.ts new file mode 100644 index 000000000..3e0a52014 --- /dev/null +++ b/web/src/pages/flow/canvas/node/hooks.ts @@ -0,0 +1,77 @@ +import get from 'lodash/get'; +import intersectionWith from 'lodash/intersectionWith'; +import isEqual from 'lodash/isEqual'; +import pick from 'lodash/pick'; +import { useEffect, useMemo, useState } from 'react'; +import { useUpdateNodeInternals } from 'reactflow'; +import { IPosition, NodeData } from '../../interface'; +import { buildNewPositionMap } from '../../utils'; + +export const useBuildCategorizeHandlePositions = ({ + data, + id, +}: { + id: string; + data: NodeData; +}) => { + const updateNodeInternals = useUpdateNodeInternals(); + const [positionMap, setPositionMap] = useState>({}); + const categoryData = useMemo( + () => get(data, 'form.category_description') ?? {}, + [data], + ); + + const positions = useMemo(() => { + return Object.keys(categoryData) + .map((x) => { + const position = positionMap[x]; + return { text: x, ...position }; + }) + .filter((x) => typeof x?.right === 'number'); + }, [categoryData, positionMap]); + + useEffect(() => { + // Cache used coordinates + setPositionMap((state) => { + // index in use + const indexesInUse = Object.values(state).map((x) => x.idx); + const categoryDataKeys = Object.keys(categoryData); + const stateKeys = Object.keys(state); + if (!isEqual(categoryDataKeys.sort(), stateKeys.sort())) { + const intersectionKeys = intersectionWith( + stateKeys, + categoryDataKeys, + (categoryDataKey, postionMapKey) => categoryDataKey === postionMapKey, + ); + const newPositionMap = buildNewPositionMap( + categoryDataKeys.filter( + (x) => !intersectionKeys.some((y) => y === x), + ), + indexesInUse, + ); + + const nextPositionMap = { + ...pick(state, intersectionKeys), + ...newPositionMap, + }; + + return nextPositionMap; + } + return state; + }); + }, [categoryData]); + + useEffect(() => { + updateNodeInternals(id); + }, [id, updateNodeInternals, positionMap]); + + return { positions }; +}; + +export const useBuildSwitchHandlePositions = ({ + data, + id, +}: { + id: string; + data: NodeData; +}) => {}; diff --git a/web/src/pages/flow/constant.tsx b/web/src/pages/flow/constant.tsx index ec652fe43..c7295b87d 100644 --- a/web/src/pages/flow/constant.tsx +++ b/web/src/pages/flow/constant.tsx @@ -485,7 +485,7 @@ export const NodeMap = { [Operator.BaiduFanyi]: 'ragNode', [Operator.QWeather]: 'ragNode', [Operator.ExeSQL]: 'ragNode', - [Operator.Switch]: 'logicNode', + [Operator.Switch]: 'categorizeNode', }; export const LanguageOptions = [ diff --git a/web/src/pages/flow/form-hooks.ts b/web/src/pages/flow/form-hooks.ts index 92e84cee4..a5a2c9ac1 100644 --- a/web/src/pages/flow/form-hooks.ts +++ b/web/src/pages/flow/form-hooks.ts @@ -1,6 +1,6 @@ import { useTranslate } from '@/hooks/common-hooks'; import { useCallback, useMemo } from 'react'; -import { Operator } from './constant'; +import { Operator, RestrictedUpstreamMap } from './constant'; import useGraphStore from './store'; const ExcludedNodesMap = { @@ -13,6 +13,7 @@ const ExcludedNodesMap = { ], [Operator.Relevant]: [Operator.Begin, Operator.Answer, Operator.Relevant], [Operator.Generate]: [Operator.Begin], + [Operator.Switch]: [Operator.Begin], }; export const useBuildFormSelectOptions = ( @@ -23,7 +24,8 @@ export const useBuildFormSelectOptions = ( const buildCategorizeToOptions = useCallback( (toList: string[]) => { - const excludedNodes: Operator[] = ExcludedNodesMap[operatorName] ?? []; + const excludedNodes: Operator[] = + RestrictedUpstreamMap[operatorName] ?? []; return nodes .filter( (x) => diff --git a/web/src/pages/flow/store.ts b/web/src/pages/flow/store.ts index a87a81919..3e818dcfd 100644 --- a/web/src/pages/flow/store.ts +++ b/web/src/pages/flow/store.ts @@ -147,7 +147,6 @@ const useGraphStore = create()( ]); } }, - addNode: (node: Node) => { set({ nodes: get().nodes.concat(node) }); }, diff --git a/web/src/pages/flow/switch-form/index.tsx b/web/src/pages/flow/switch-form/index.tsx index f9564a7cb..c302ef262 100644 --- a/web/src/pages/flow/switch-form/index.tsx +++ b/web/src/pages/flow/switch-form/index.tsx @@ -1,7 +1,9 @@ import { CloseOutlined } from '@ant-design/icons'; -import { Button, Card, Form, Input, Typography } from 'antd'; +import { Button, Card, Form, Input, Select, Typography } from 'antd'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { Operator } from '../constant'; +import { useBuildFormSelectOptions } from '../form-hooks'; import { IOperatorForm } from '../interface'; const subLabelCol = { @@ -12,8 +14,16 @@ const subWrapperCol = { span: 17, }; -const SwitchForm: React.FC = ({ form, onValuesChange }: IOperatorForm) => { +const SwitchForm: React.FC = ({ + form, + onValuesChange, + nodeId, +}: IOperatorForm) => { const { t } = useTranslation(); + const buildCategorizeToOptions = useBuildFormSelectOptions( + Operator.Categorize, + nodeId, + ); return (
{ onValuesChange={onValuesChange} > - + @@ -55,9 +65,8 @@ const SwitchForm: React.FC = ({ form, onValuesChange }: IOperatorForm) => { - +