浏览代码

feat: Build the edges of Switch by form data #1739 (#2022)

### 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)
tags/v0.10.0
balibabu 1年前
父节点
当前提交
96438ca821
没有帐户链接到提交者的电子邮件

+ 6
- 2
web/src/pages/flow/canvas/node/hooks.ts 查看文件

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,
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 };
}) })

+ 10
- 12
web/src/pages/flow/categorize-form/dynamic-categorize.tsx 查看文件



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 = ({
]} ]}
> >
<NameInput <NameInput
otherNames={getOtherFieldValues(form, field, 'name')}
otherNames={getOtherFieldValues(
form,
'items',
field,
'name',
)}
validate={(errors: string[]) => validate={(errors: string[]) =>
form.setFields([ form.setFields([
{ {
<Select <Select
allowClear allowClear
options={buildCategorizeToOptions( options={buildCategorizeToOptions(
getOtherFieldValues(form, field, 'to'),
getOtherFieldValues(form, 'items', field, 'to'),
)} )}
/> />
</Form.Item> </Form.Item>
); );
}} }}
</Form.List> </Form.List>

{/* <Form.Item noStyle shouldUpdate>
{() => (
<Typography>
<pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>
</Typography>
)}
</Form.Item> */}
</> </>
); );
}; };

+ 31
- 1
web/src/pages/flow/hooks.ts 查看文件

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';
[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);
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;
} }
buildCategorizeEdgesByFormData, buildCategorizeEdgesByFormData,
getNode, getNode,
buildRelevantEdgesByFormData, buildRelevantEdgesByFormData,
buildSwitchEdgesByFormData,
]); ]);
}; };

+ 32
- 10
web/src/pages/flow/store.ts 查看文件

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>[];
'to', 'to',
]); ]);
break; break;
// case Operator.Switch:
// if (sourceHandle)
// updateNodeForm(source, target, [
// 'conditions',
// sourceHandle,
// 'to',
// ]);
// break;
case Operator.Switch: {
if (sourceHandle) {
const operatorIndex = getOperatorIndex(sourceHandle);
if (operatorIndex) {
updateNodeForm(source, target, [
'conditions',
operatorIndex,
'to',
]);
}
}
break;
}
default: default:
break; break;
} }
// 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),
'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;
} }

+ 23
- 11
web/src/pages/flow/switch-form/index.tsx 查看文件

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,
span: 17, span: 17,
}; };


const SwitchForm: React.FC = ({
form,
onValuesChange,
nodeId,
}: IOperatorForm) => {
const SwitchForm = ({ onValuesChange, node, form }: IOperatorForm) => {
const { t } = useTranslation(); const { t } = useTranslation();
const buildCategorizeToOptions = useBuildFormSelectOptions( const buildCategorizeToOptions = useBuildFormSelectOptions(
Operator.Categorize,
nodeId,
Operator.Switch,
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 }}
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 />
</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']}>

+ 26
- 1
web/src/pages/flow/utils.ts 查看文件

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';
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}`;
};

正在加载...
取消
保存