Skip to content

Commit

Permalink
fix:并行节点数据结构完善+handleID问题处理
Browse files Browse the repository at this point in the history
  • Loading branch information
昔梦 committed Dec 24, 2024
1 parent 61363a0 commit 9116f36
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 59 deletions.
103 changes: 61 additions & 42 deletions docs/xflow/demo/parallelNode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,94 @@ import React from 'react'
export default () => {
const nodes = [
{
type: 'Start',

id: '1',
id: 'mcelcsg6pinydoy7',
type: 'Parallel',
data: {
list: [
{
_parallelId: 'parallel_30ds0x3evus7ogo2',
value: '事件1',
},
{
_parallelId: 'parallel_m1l276eelcgn7s1p',
value: '事件2',
_parallelTitle: '自定义事件名称'
},
],
},
position: {
x: -40,
y: 217.02097759552555,
y: 281.25,
},
},
{
type: 'End',
position:{
"x": 733,
"y": 70
},
id: '2',
id: '4m9tee00n819nyyy',
type: 'tool',
position: {
x: 400,
y: 227.5,
},
},
{
id: 'qga2pm66shrcn0ir',
id: 'j0kufl0o4fca4ee9',
type: 'knowledge',

position: {
"x": 320,
"y": 228
x: 312.5,
y: 438.75,
},
type: 'Parallel',
data: {
list: [
{
value: '并行事件1',
},
{
value: '并行事件2',
},
],

},
{
id: 'w4be9edh4bhdlokm',
type: 'Start',
position: {
x: -379.21875,
y: 348.75,
},
},
{
id: 'z0ps4v0c8xkvuu5c',
id: '3qloq2p1x3wcwbzg',
type: 'End',
position: {
"x": 640,
"y": 461
x: 675,
y: 360,
},
type: 'Code',
},
];

const edges = [
{
source: '1',
target: 'qga2pm66shrcn0ir',
id: 'xy-edge__1-qga2pm66shrcn0ir',
id: 'ky0eedrd6t2hqq81',
source: 'mcelcsg6pinydoy7',
target: '4m9tee00n819nyyy',
sourceHandle: 'parallel_30ds0x3evus7ogo2',
},
{
id: '7tm5a339lj94ugtn',
source: 'mcelcsg6pinydoy7',
target: 'j0kufl0o4fca4ee9',
sourceHandle: 'parallel_m1l276eelcgn7s1p',
},
{
source: 'qga2pm66shrcn0ir',
sourceHandle: 'id_0',
target: '2',
id: 'xy-edge__qga2pm66shrcn0irid_0-2',
type: 'buttonedge',
source: 'w4be9edh4bhdlokm',
target: 'mcelcsg6pinydoy7',
id: 'xy-edge__w4be9edh4bhdlokm-mcelcsg6pinydoy7',
},
{
source: 'qga2pm66shrcn0ir',
sourceHandle: 'id_1',
target: 'z0ps4v0c8xkvuu5c',
id: 'xy-edge__qga2pm66shrcn0irid_1-z0ps4v0c8xkvuu5c',
type: 'buttonedge',
source: '4m9tee00n819nyyy',
target: '3qloq2p1x3wcwbzg',
id: 'xy-edge__4m9tee00n819nyyy-3qloq2p1x3wcwbzg',
},
{
source: 'z0ps4v0c8xkvuu5c',
target: '2',
id: 'xy-edge__z0ps4v0c8xkvuu5c-2',
type: 'buttonedge',
source: 'j0kufl0o4fca4ee9',
target: '3qloq2p1x3wcwbzg',
id: 'xy-edge__j0kufl0o4fca4ee9-3qloq2p1x3wcwbzg',
},
];

return (
<div style={{ height: '600px' }}>
<XFlow
Expand Down
4 changes: 2 additions & 2 deletions docs/xflow/demo/parallelNode/setting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export default [
{
title: '开始',
type: 'Start',
hidden: true,
// hidden: true,
targetHandleHidden: true,
icon: {
type: 'icon-start',
Expand Down Expand Up @@ -71,7 +71,7 @@ export default [
{
title: '结束',
type: 'End',
hidden: true,
// hidden: true,
sourceHandleHidden: true,
icon: {
type: 'icon-end',
Expand Down
22 changes: 21 additions & 1 deletion docs/xflow/nodeBuildIn.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,25 @@ group:
<code src="./demo/switchNode/customSwitchNode/index.tsx"></code>
## 并行节点
内置并行节点,可以直接设置type为`Parallel`使用,并行节点的数据格式为`list:[{value:'事件1'}]`
内置并行节点,可以直接设置type为`Parallel`使用,并行节点的数据格式为`data:{list:[{value:"条件1",_parallelId:"parallel_${随机数}"}]}`,`_parallelId`为边数据的`sourceHandle`,以便条件和边一一对应。
条件节点的每个连接头只能连接一个节点,不能连接多个节点,如果要更换节点,可以通过删除已连接节点或者在连接线上新增节点的方式更换目标节点。
并行节点可通过`data.list[]._parallelTitle`自定义事件名称的描述,`_parallelTitle`仅支持`string`类型,比如:
```js
data: {
list: [
{
_parallelId: 'parallel_30ds0x3evus7ogo2',
value: '事件1',
},
{
_parallelId: 'parallel_m1l276eelcgn7s1p',
value: '事件2',
_parallelTitle:'自定义事件名称'// 只能传字符串
},
],
}
```
<code src="./demo/parallelNode/index.tsx"></code>
16 changes: 14 additions & 2 deletions packages/x-flow/src/components/NodeEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React, { FC, useContext, useEffect, useState } from 'react';
import { shallow } from 'zustand/shallow';
import { useStore } from '../../hooks/useStore';
import { ConfigContext } from '../../models/context';
import { uuid } from '../../utils';
import { safeJsonStringify, uuid } from '../../utils';

interface INodeEditorProps {
data: any;
Expand Down Expand Up @@ -43,7 +43,7 @@ const NodeEditor: FC<INodeEditorProps> = (props: any) => {
} else {
//可能为内置schema或者是没有
}
}, [JSON.stringify(data), id]);
}, [safeJsonStringify(data), id]);

const handleNodeValueChange = debounce((data: any) => {
const newNodes = produce(nodes, draft => {
Expand All @@ -69,6 +69,18 @@ const NodeEditor: FC<INodeEditorProps> = (props: any) => {
}
}
});
} else if (node?.data?._nodeType === 'Parallel' && data?.list?.length) {
data['list'] = data?.list?.map((item, index) => {
if (item?._parallelId) {
return item;
} else {
if (node?.data?.list[index]?._parallelId) {
return { ...item, _parallelId: node?.data?.list[index]?._parallelId };
} else {
return { ...item, _parallelId: `parallel_${uuid()}` };
}
}
});
}
node.data = { ...node.data, ...data };
}
Expand Down
4 changes: 3 additions & 1 deletion packages/x-flow/src/components/NodesPopover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ export default forwardRef((props: any, popoverRef) => {

const handCreateNode = useCallback<any>(({ type }) => {
if (type === 'Switch') {
addNode({ _nodeType: type, list: [{ '_conditionId':`condition_${uuid()}`}] });
addNode({ _nodeType: type, list: [{ '_conditionId':`${uuid()}`}] });
} else if (type === 'Parallel') {
addNode({ _nodeType: type, list: [{ _parallelId: `parallel_${uuid()}` }, { _parallelId: `parallel_${uuid()}` }] });
} else {
addNode({ _nodeType: type });
}
Expand Down
4 changes: 2 additions & 2 deletions packages/x-flow/src/components/PanelContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { shallow } from 'zustand/shallow';
import { useStore } from '../../hooks/useStore';
import { ConfigContext } from '../../models/context';
import createIconFont from '../../utils/createIconFont';
import { getAntdVersion } from '../../utils';
import { getAntdVersion, safeJsonStringify } from '../../utils';
import IconView from '../IconView';
import './index.less';

Expand Down Expand Up @@ -65,7 +65,7 @@ const Panel: FC<IPanelProps> = (props: IPanelProps) => {
useEffect(() => {
setDescVal(data?.desc);
setTitleVal(data?.title || nodeSetting?.title);
}, [JSON.stringify(data), id]);
}, [safeJsonStringify(data), id]);

const Icon = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { Space, Typography } from 'antd';
import React, { memo } from 'react';
import SourceHandle from '../../components/CustomNode/sourceHandle';
import './index.less';
import { uuid } from '../../utils';
import { useStore } from '../../hooks/useStore';
import { shallow } from 'zustand/shallow';


const { Paragraph } = Typography;

Expand All @@ -16,21 +20,39 @@ export default memo((props: any) => {
CustomNodeWidget,
} = props;

const { nodes, edges } = useStore(
(state: any) => ({
nodes: state.nodes,
edges: state.edges,
mousePosition: state.mousePosition,
addNodes: state.addNodes,
addEdges: state.addEdges,
onEdgesChange: state.onEdgesChange,
}),
shallow
);

return (
<Space direction='vertical' className="node-switch-widget" size={5}>
{(data?.list || [{},{}])?.map((item, index) => (
{(data?.list || [{ _parallelId: `parallel_${uuid()}` }, { _parallelId: `parallel_${uuid()}` }])?.map((item, index) => (
<div className="node-switch-widget-item" key={index}>
<div className="item-header">
<div className="item-title">
{`事件${index}` }
{item?._parallelTitle ?? `事件${index}` }
</div>
<SourceHandle
position={position}
isConnectable={isConnectable}
isConnectable={
(edges || [])?.filter(
flow => flow?.sourceHandle === item?._parallelId
)?.length === 0
}
selected={selected}
isHovered={isHovered}
handleAddNode={handleAddNode}
id={`id_${index}`}
handleAddNode={data => {
handleAddNode(data, item?._parallelId);
}}
id={item?._parallelId}
className="item-handle"
/>
</div>
Expand Down
4 changes: 2 additions & 2 deletions packages/x-flow/src/nodes/node-parallel/setting/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import FormRender, { Schema, useForm } from 'form-render';
import React, { memo, useEffect } from 'react';
import '../index.less';
import { safeJsonStringify } from '../../../utils';

interface INodeSwitchSettingPorps {
onChange: (val: any) => void;
Expand Down Expand Up @@ -41,15 +42,14 @@ export default memo((props: INodeSwitchSettingPorps) => {

const watch = {
'#': (allValues: any) => {

onChange({ ...allValues });
},
};

useEffect(() => {
form.resetFields();
form.setValues(value || {});
}, [JSON.stringify(value)]);
}, [safeJsonStringify(value)]);

return (
<FormRender
Expand Down
3 changes: 2 additions & 1 deletion packages/x-flow/src/nodes/node-switch/setting/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import FormRender, { Schema, useForm } from 'form-render';
import React, { memo, useEffect } from 'react';
import '../index.less';
import { safeJsonStringify } from '../../../utils';

interface INodeSwitchSettingPorps {
onChange: (val: any) => void;
Expand Down Expand Up @@ -48,7 +49,7 @@ export default memo((props: INodeSwitchSettingPorps) => {
useEffect(() => {
form.resetFields();
form.setValues(value || {});
}, [JSON.stringify(value)]);
}, [safeJsonStringify(value)]);

return (
<FormRender
Expand Down
41 changes: 40 additions & 1 deletion packages/x-flow/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,43 @@ export const getAntdVersion = () => {
} else {
return 'V4'
}
}
}

// 安全的JSON.stringify
export function safeJsonStringify(obj: Object) {
const seen = new WeakSet();

function replacer(key, value) {
// 循环引用
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]';
}
seen.add(value);
}

// React
if (value && typeof value === 'object' && value._owner) {
return '[React Element]';
}

// BigInt
if (typeof value === 'bigint') {
return value.toString();
}

// 其他无法序列化的类型
if (typeof value === 'function') {
return `[Function: ${value.name || 'anonymous'}]`;
}

return value;
}

try {
return JSON.stringify(obj, replacer, 2);
} catch (error) {
console.error('json.stringify error', error);
return null;
}
}

0 comments on commit 9116f36

Please sign in to comment.