mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-13 21:25:53 +08:00
### What problem does this PR solve? feat: Build the edges of Switch by form data #1739 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
7927d80a84
commit
96438ca821
@ -4,7 +4,11 @@ import { useEffect, useMemo, useState } from 'react';
|
|||||||
import { useUpdateNodeInternals } from 'reactflow';
|
import { useUpdateNodeInternals } from 'reactflow';
|
||||||
import { Operator } from '../../constant';
|
import { Operator } from '../../constant';
|
||||||
import { IPosition, NodeData } from '../../interface';
|
import { IPosition, NodeData } from '../../interface';
|
||||||
import { buildNewPositionMap, isKeysEqual } from '../../utils';
|
import {
|
||||||
|
buildNewPositionMap,
|
||||||
|
generateSwitchHandleText,
|
||||||
|
isKeysEqual,
|
||||||
|
} from '../../utils';
|
||||||
|
|
||||||
export const useBuildCategorizeHandlePositions = ({
|
export const useBuildCategorizeHandlePositions = ({
|
||||||
data,
|
data,
|
||||||
@ -32,7 +36,7 @@ export const useBuildCategorizeHandlePositions = ({
|
|||||||
const position = positionMap[x];
|
const position = positionMap[x];
|
||||||
let text = x;
|
let text = x;
|
||||||
if (operatorName === Operator.Switch) {
|
if (operatorName === Operator.Switch) {
|
||||||
text = `Item ${idx + 1}`;
|
text = generateSwitchHandleText(idx);
|
||||||
}
|
}
|
||||||
return { text, ...position };
|
return { text, ...position };
|
||||||
})
|
})
|
||||||
|
@ -28,14 +28,15 @@ interface INameInputProps {
|
|||||||
|
|
||||||
const getOtherFieldValues = (
|
const getOtherFieldValues = (
|
||||||
form: FormInstance,
|
form: FormInstance,
|
||||||
|
formListName: string = 'items',
|
||||||
field: FormListFieldData,
|
field: FormListFieldData,
|
||||||
latestField: string,
|
latestField: string,
|
||||||
) =>
|
) =>
|
||||||
(form.getFieldValue(['items']) ?? [])
|
(form.getFieldValue([formListName]) ?? [])
|
||||||
.map((x: any) => x[latestField])
|
.map((x: any) => x[latestField])
|
||||||
.filter(
|
.filter(
|
||||||
(x: string) =>
|
(x: string) =>
|
||||||
x !== form.getFieldValue(['items', field.name, latestField]),
|
x !== form.getFieldValue([formListName, field.name, latestField]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const NameInput = ({
|
const NameInput = ({
|
||||||
@ -132,7 +133,12 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<NameInput
|
<NameInput
|
||||||
otherNames={getOtherFieldValues(form, field, 'name')}
|
otherNames={getOtherFieldValues(
|
||||||
|
form,
|
||||||
|
'items',
|
||||||
|
field,
|
||||||
|
'name',
|
||||||
|
)}
|
||||||
validate={(errors: string[]) =>
|
validate={(errors: string[]) =>
|
||||||
form.setFields([
|
form.setFields([
|
||||||
{
|
{
|
||||||
@ -159,7 +165,7 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|||||||
<Select
|
<Select
|
||||||
allowClear
|
allowClear
|
||||||
options={buildCategorizeToOptions(
|
options={buildCategorizeToOptions(
|
||||||
getOtherFieldValues(form, field, 'to'),
|
getOtherFieldValues(form, 'items', field, 'to'),
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -173,14 +179,6 @@ const DynamicCategorize = ({ nodeId }: IProps) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
</Form.List>
|
</Form.List>
|
||||||
|
|
||||||
{/* <Form.Item noStyle shouldUpdate>
|
|
||||||
{() => (
|
|
||||||
<Typography>
|
|
||||||
<pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</Form.Item> */}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -53,10 +53,11 @@ import {
|
|||||||
initialSwitchValues,
|
initialSwitchValues,
|
||||||
initialWikipediaValues,
|
initialWikipediaValues,
|
||||||
} from './constant';
|
} from './constant';
|
||||||
import { ICategorizeForm, IRelevantForm } from './interface';
|
import { ICategorizeForm, IRelevantForm, ISwitchForm } from './interface';
|
||||||
import useGraphStore, { RFState } from './store';
|
import useGraphStore, { RFState } from './store';
|
||||||
import {
|
import {
|
||||||
buildDslComponentsByGraph,
|
buildDslComponentsByGraph,
|
||||||
|
generateSwitchHandleText,
|
||||||
receiveMessageError,
|
receiveMessageError,
|
||||||
replaceIdWithText,
|
replaceIdWithText,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
@ -498,6 +499,31 @@ export const useWatchNodeFormDataChange = () => {
|
|||||||
[setEdgesByNodeId],
|
[setEdgesByNodeId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const buildSwitchEdgesByFormData = useCallback(
|
||||||
|
(nodeId: string, form: ISwitchForm) => {
|
||||||
|
// add
|
||||||
|
// delete
|
||||||
|
// edit
|
||||||
|
const conditions = form.conditions;
|
||||||
|
const downstreamEdges = conditions.reduce<Edge[]>((pre, _, idx) => {
|
||||||
|
const target = conditions[idx]?.to;
|
||||||
|
if (target) {
|
||||||
|
pre.push({
|
||||||
|
id: uuid(),
|
||||||
|
source: nodeId,
|
||||||
|
target,
|
||||||
|
sourceHandle: generateSwitchHandleText(idx),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return pre;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
setEdgesByNodeId(nodeId, downstreamEdges);
|
||||||
|
},
|
||||||
|
[setEdgesByNodeId],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
const currentNode = getNode(node.id);
|
const currentNode = getNode(node.id);
|
||||||
@ -510,6 +536,9 @@ export const useWatchNodeFormDataChange = () => {
|
|||||||
case Operator.Categorize:
|
case Operator.Categorize:
|
||||||
buildCategorizeEdgesByFormData(node.id, form as ICategorizeForm);
|
buildCategorizeEdgesByFormData(node.id, form as ICategorizeForm);
|
||||||
break;
|
break;
|
||||||
|
case Operator.Switch:
|
||||||
|
buildSwitchEdgesByFormData(node.id, form as ISwitchForm);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -519,5 +548,6 @@ export const useWatchNodeFormDataChange = () => {
|
|||||||
buildCategorizeEdgesByFormData,
|
buildCategorizeEdgesByFormData,
|
||||||
getNode,
|
getNode,
|
||||||
buildRelevantEdgesByFormData,
|
buildRelevantEdgesByFormData,
|
||||||
|
buildSwitchEdgesByFormData,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
@ -23,7 +23,7 @@ import { devtools } from 'zustand/middleware';
|
|||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import { Operator } from './constant';
|
import { Operator } from './constant';
|
||||||
import { NodeData } from './interface';
|
import { NodeData } from './interface';
|
||||||
import { isEdgeEqual } from './utils';
|
import { getOperatorIndex, isEdgeEqual } from './utils';
|
||||||
|
|
||||||
export type RFState = {
|
export type RFState = {
|
||||||
nodes: Node<NodeData>[];
|
nodes: Node<NodeData>[];
|
||||||
@ -184,14 +184,19 @@ const useGraphStore = create<RFState>()(
|
|||||||
'to',
|
'to',
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
// case Operator.Switch:
|
case Operator.Switch: {
|
||||||
// if (sourceHandle)
|
if (sourceHandle) {
|
||||||
// updateNodeForm(source, target, [
|
const operatorIndex = getOperatorIndex(sourceHandle);
|
||||||
// 'conditions',
|
if (operatorIndex) {
|
||||||
// sourceHandle,
|
updateNodeForm(source, target, [
|
||||||
// 'to',
|
'conditions',
|
||||||
// ]);
|
operatorIndex,
|
||||||
// break;
|
'to',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -201,7 +206,11 @@ const useGraphStore = create<RFState>()(
|
|||||||
// Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes
|
// Delete the edge on the classification node or relevant node anchor when the anchor is connected to other nodes
|
||||||
const { edges, getOperatorTypeFromId, deleteEdgeById } = get();
|
const { edges, getOperatorTypeFromId, deleteEdgeById } = get();
|
||||||
// the node containing the anchor
|
// the node containing the anchor
|
||||||
const anchoredNodes = [Operator.Categorize, Operator.Relevant];
|
const anchoredNodes = [
|
||||||
|
Operator.Categorize,
|
||||||
|
Operator.Relevant,
|
||||||
|
Operator.Switch,
|
||||||
|
];
|
||||||
if (
|
if (
|
||||||
anchoredNodes.some(
|
anchoredNodes.some(
|
||||||
(x) => x === getOperatorTypeFromId(connection.source),
|
(x) => x === getOperatorTypeFromId(connection.source),
|
||||||
@ -265,6 +274,19 @@ const useGraphStore = create<RFState>()(
|
|||||||
'to',
|
'to',
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
case Operator.Switch: {
|
||||||
|
if (sourceHandle) {
|
||||||
|
const operatorIndex = getOperatorIndex(sourceHandle);
|
||||||
|
if (operatorIndex) {
|
||||||
|
updateNodeForm(source, undefined, [
|
||||||
|
'conditions',
|
||||||
|
operatorIndex,
|
||||||
|
'to',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Button, Card, Form, Input, Select, Typography } from 'antd';
|
import { Button, Card, Form, Input, Select, Typography } from 'antd';
|
||||||
import React from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Operator } from '../constant';
|
import { Operator } from '../constant';
|
||||||
import { useBuildFormSelectOptions } from '../form-hooks';
|
import { useBuildFormSelectOptions } from '../form-hooks';
|
||||||
import { IOperatorForm } from '../interface';
|
import { IOperatorForm, ISwitchForm } from '../interface';
|
||||||
|
import { getOtherFieldValues } from '../utils';
|
||||||
|
|
||||||
const subLabelCol = {
|
const subLabelCol = {
|
||||||
span: 7,
|
span: 7,
|
||||||
@ -14,17 +14,20 @@ const subWrapperCol = {
|
|||||||
span: 17,
|
span: 17,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SwitchForm: React.FC = ({
|
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
|
||||||
form,
|
|
||||||
onValuesChange,
|
|
||||||
nodeId,
|
|
||||||
}: IOperatorForm) => {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
const buildCategorizeToOptions = useBuildFormSelectOptions(
|
||||||
Operator.Categorize,
|
Operator.Switch,
|
||||||
nodeId,
|
node?.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getSelectedConditionTos = () => {
|
||||||
|
const conditions: ISwitchForm['conditions'] =
|
||||||
|
form?.getFieldValue('conditions');
|
||||||
|
|
||||||
|
return conditions?.filter((x) => !!x).map((x) => x?.to) ?? [];
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Form
|
||||||
labelCol={{ span: 8 }}
|
labelCol={{ span: 8 }}
|
||||||
@ -36,7 +39,10 @@ const SwitchForm: React.FC = ({
|
|||||||
onValuesChange={onValuesChange}
|
onValuesChange={onValuesChange}
|
||||||
>
|
>
|
||||||
<Form.Item label={t('flow.to')} name={['end_cpn_id']}>
|
<Form.Item label={t('flow.to')} name={['end_cpn_id']}>
|
||||||
<Select options={buildCategorizeToOptions([])} />
|
<Select
|
||||||
|
allowClear
|
||||||
|
options={buildCategorizeToOptions(getSelectedConditionTos())}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t('flow.no')} name={['no']}>
|
<Form.Item label={t('flow.no')} name={['no']}>
|
||||||
<Input />
|
<Input />
|
||||||
@ -65,7 +71,13 @@ const SwitchForm: React.FC = ({
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
|
<Form.Item label={t('flow.to')} name={[field.name, 'to']}>
|
||||||
<Select options={buildCategorizeToOptions([])} />
|
<Select
|
||||||
|
allowClear
|
||||||
|
options={buildCategorizeToOptions([
|
||||||
|
form?.getFieldValue('end_cpn_id'),
|
||||||
|
...getOtherFieldValues(form!, 'conditions', field, 'to'),
|
||||||
|
])}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Items">
|
<Form.Item label="Items">
|
||||||
<Form.List name={[field.name, 'items']}>
|
<Form.List name={[field.name, 'items']}>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { DSLComponents } from '@/interfaces/database/flow';
|
import { DSLComponents } from '@/interfaces/database/flow';
|
||||||
import { removeUselessFieldsFromValues } from '@/utils/form';
|
import { removeUselessFieldsFromValues } from '@/utils/form';
|
||||||
|
import { FormInstance, FormListFieldData } from 'antd';
|
||||||
import { humanId } from 'human-id';
|
import { humanId } from 'human-id';
|
||||||
import { curry, intersectionWith, isEqual, sample } from 'lodash';
|
import { curry, get, intersectionWith, isEqual, sample } from 'lodash';
|
||||||
import pipe from 'lodash/fp/pipe';
|
import pipe from 'lodash/fp/pipe';
|
||||||
import isObject from 'lodash/isObject';
|
import isObject from 'lodash/isObject';
|
||||||
import { Edge, Node, Position } from 'reactflow';
|
import { Edge, Node, Position } from 'reactflow';
|
||||||
@ -209,3 +210,27 @@ export const buildNewPositionMap = (
|
|||||||
export const isKeysEqual = (currentKeys: string[], previousKeys: string[]) => {
|
export const isKeysEqual = (currentKeys: string[], previousKeys: string[]) => {
|
||||||
return isEqual(currentKeys.sort(), previousKeys.sort());
|
return isEqual(currentKeys.sort(), previousKeys.sort());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getOperatorIndex = (handleTitle: string) => {
|
||||||
|
return handleTitle.split(' ').at(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the value of other forms except itself
|
||||||
|
export const getOtherFieldValues = (
|
||||||
|
form: FormInstance,
|
||||||
|
formListName: string = 'items',
|
||||||
|
field: FormListFieldData,
|
||||||
|
latestField: string,
|
||||||
|
) =>
|
||||||
|
(form.getFieldValue([formListName]) ?? [])
|
||||||
|
.map((x: any) => {
|
||||||
|
return get(x, latestField);
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
(x: string) =>
|
||||||
|
x !== form.getFieldValue([formListName, field.name, latestField]),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const generateSwitchHandleText = (idx: number) => {
|
||||||
|
return `Item ${idx + 1}`;
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user