Skip to content

Commit 7c0beb1

Browse files
author
zhanbo.lh
committed
feat: 迭代开发
1 parent 715accb commit 7c0beb1

File tree

6 files changed

+333
-243
lines changed

6 files changed

+333
-243
lines changed

docs/xflow/index.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@ import data from './data/basic';
5050
const nodeMenus = [
5151
{
5252
title: 'Input',
53-
type: 'Input',
53+
type: 'Start',
5454
icon: {
5555
type: 'icon-start',
5656
bgColor: '#17B26A',
5757
}
5858
},
5959
{
6060
title: 'Output',
61-
type: 'Output',
61+
type: 'End',
6262
icon: {
6363
type: 'icon-end',
6464
bgColor: '#F79009',
@@ -160,11 +160,15 @@ export default () => {
160160
}
161161
];
162162

163+
const edges = [
164+
{ source: '1', target: '2', id: '234123' }
165+
]
166+
163167
return (
164168
<div style={{ height: '600px' }}>
165169
<XFlow
166170
nodes={nodes}
167-
edges={[]}
171+
edges={edges}
168172
nodeMenus={nodeMenus}
169173
/>
170174
</div>

packages/x-flow/src/components/CustomEdge/index.less

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,14 @@
1111
align-items: center;
1212
}
1313

14-
.icon-box {
15-
width: 20px;
16-
height: 20px;
17-
border-radius: 20px;
14+
.line-icon-box {
15+
width: 16px;
16+
height: 16px;
17+
border-radius: 16px;
1818
display: flex;
1919
align-items: center;
2020
justify-content: center;
21-
background: #c6c6c6;
22-
visibility: hidden;
23-
}
24-
25-
.icon-box:hover {
2621
background: #296dff;
27-
}
28-
}
29-
30-
.custom-edge-line:hover {
31-
.icon-box {
3222
visibility: visible;
3323
}
3424
}
Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,27 @@
1-
import React, { memo, useContext } from 'react';
1+
import React, { memo, useState } from 'react';
22
import { PlusOutlined, CloseOutlined } from '@ant-design/icons';
33
import { BezierEdge, EdgeLabelRenderer, getBezierPath, useReactFlow } from '@xyflow/react';
44
import { useShallow } from 'zustand/react/shallow';
55
import produce from 'immer';
66
import { uuid } from '../../core/utils';
77
import useStore from '../../core/store';
8-
import { ConfigContext } from '../../models/context';
98
import NodeSelectPopover from '../NodeSelectPopover';
109
import './index.less';
1110

1211
export default memo((edge: any) => {
1312
const {
14-
label,
1513
id,
14+
selected,
1615
sourceX,
1716
sourceY,
1817
targetX,
1918
targetY,
20-
data,
21-
selected,
2219
source,
2320
target,
24-
layout,
2521
} = edge;
2622

27-
const configCtx: any = useContext(ConfigContext);
28-
2923
const reactflow = useReactFlow();
24+
const [isHovered, setIsHovered] = useState(false);
3025
const [edgePath, labelX, labelY] = getBezierPath({
3126
sourceX,
3227
sourceY,
@@ -36,29 +31,58 @@ export default memo((edge: any) => {
3631

3732
const {
3833
nodes,
34+
edges,
3935
setNodes,
36+
setEdges,
4037
mousePosition,
38+
onEdgesChange,
39+
layout,
4140
} = useStore(
4241
useShallow((state: any) => ({
42+
layout: state.layout,
4343
nodes: state.nodes,
44+
edges: state.edges,
4445
mousePosition: state.mousePosition,
45-
setNodes: state.setNodes
46+
setNodes: state.setNodes,
47+
setEdges: state.setEdges,
48+
onEdgesChange: state.onEdgesChange
4649
}))
4750
);
4851

4952
const handleAddNode = (data: any) => {
5053
const { screenToFlowPosition } = reactflow;
5154
const { x, y } = screenToFlowPosition({ x: mousePosition.pageX, y: mousePosition.pageY });
5255

56+
57+
const targetId = uuid();
5358
const newNodes = produce(nodes, (draft: any) => {
5459
draft.push({
55-
id: uuid(),
60+
id: targetId,
5661
type: 'custom',
5762
data,
5863
position: { x, y }
5964
});
6065
});
66+
67+
const newEdges = produce(edges, (draft: any) => {
68+
draft.push(...[
69+
{
70+
id: uuid(),
71+
source,
72+
target: targetId,
73+
},
74+
{
75+
id: uuid(),
76+
source: targetId,
77+
target,
78+
}
79+
])
80+
81+
});
82+
6183
setNodes(newNodes);
84+
setEdges(newEdges);
85+
onEdgesChange([{ id, type: 'remove' }]);
6286
};
6387

6488
let edgeExtra: any = {
@@ -73,32 +97,36 @@ export default memo((edge: any) => {
7397
}
7498

7599
return (
76-
<BezierEdge
77-
{...edge}
78-
{...edgeExtra}
79-
selected={false}
80-
edgePath={edgePath}
81-
label={
82-
<EdgeLabelRenderer>
83-
<div
84-
className='custom-edge-line'
85-
style={{
86-
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
87-
}}
88-
>
89-
<div className='line-content'>
90-
<div className='icon-box'>
91-
<CloseOutlined style={{ color: '#fff', fontSize: 12 }} />
100+
<g
101+
onMouseEnter={() => setIsHovered(true)}
102+
onMouseLeave={() => setIsHovered(false)}
103+
>
104+
<BezierEdge
105+
{...edge}
106+
{...edgeExtra}
107+
edgePath={edgePath}
108+
label={
109+
isHovered && <EdgeLabelRenderer>
110+
<div
111+
className='custom-edge-line'
112+
style={{
113+
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
114+
}}
115+
>
116+
<div className='line-content'>
117+
<div className='line-icon-box' onClick={() => onEdgesChange([{ id, type: 'remove' }])}>
118+
<CloseOutlined style={{ color: '#fff', fontSize: 10 }} />
119+
</div>
120+
<NodeSelectPopover placement='right' addNode={handleAddNode}>
121+
<div className='line-icon-box'>
122+
<PlusOutlined style={{ color: '#fff', fontSize: 10 }} />
123+
</div>
124+
</NodeSelectPopover>
92125
</div>
93-
<NodeSelectPopover placement='right' addNode={handleAddNode}>
94-
<div className='icon-box'>
95-
<PlusOutlined style={{ color: '#fff', fontSize: 12 }} />
96-
</div>
97-
</NodeSelectPopover>
98126
</div>
99-
</div>
100-
</EdgeLabelRenderer>
101-
}
102-
/>
127+
</EdgeLabelRenderer>
128+
}
129+
/>
130+
</g>
103131
);
104132
})

packages/x-flow/src/components/CustomNode/index.tsx

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import React, { memo, useContext, useState } from 'react';
22
import { Tooltip } from 'antd';
33
import { PlusOutlined } from '@ant-design/icons';
44
import classNames from 'classnames';
5-
import { Handle, Position } from '@xyflow/react';
6-
import { capitalize } from '../../core/utils';
5+
import produce from 'immer';
6+
import { useShallow } from 'zustand/react/shallow';
7+
import { Handle, Position, useReactFlow } from '@xyflow/react';
8+
import useStore from '../../core/store';
9+
import { capitalize, uuid } from '../../core/utils';
710
import { ConfigContext } from '../../models/context';
11+
import NodeSelectPopover from '../NodeSelectPopover';
812
import './index.less';
913

1014
export default memo((props: any) => {
@@ -13,6 +17,49 @@ export default memo((props: any) => {
1317
const NodeWidget = configCtx?.nodeWidgets[`${capitalize(type)}Node`];
1418

1519
const [isHovered, setIsHovered] = useState(false);
20+
const reactflow = useReactFlow();
21+
const {
22+
edges,
23+
nodes,
24+
setNodes,
25+
setEdges,
26+
mousePosition,
27+
} = useStore(
28+
useShallow((state: any) => ({
29+
nodes: state.nodes,
30+
edges: state.edges,
31+
mousePosition: state.mousePosition,
32+
setNodes: state.setNodes,
33+
setEdges: state.setEdges,
34+
onEdgesChange: state.onEdgesChange
35+
}))
36+
);
37+
38+
// 增加节点并进行联系
39+
const handleAddNode = (data: any) => {
40+
const { screenToFlowPosition } = reactflow;
41+
const { x, y } = screenToFlowPosition({ x: mousePosition.pageX + 100, y: mousePosition.pageY + 100 });
42+
const targetId = uuid();
43+
44+
const newNodes = produce(nodes, (draft: any) => {
45+
draft.push({
46+
id: targetId,
47+
type: 'custom',
48+
data,
49+
position: { x, y }
50+
});
51+
});
52+
const newEdges = produce(edges, (draft: any) => {
53+
draft.push({
54+
id: uuid(),
55+
source: id,
56+
target: targetId,
57+
})
58+
});
59+
setNodes(newNodes);
60+
setEdges(newEdges);
61+
};
62+
1663

1764
let targetPosition = Position.Left;
1865
let sourcePosition = Position.Right;
@@ -30,7 +77,7 @@ export default memo((props: any) => {
3077
onMouseEnter={() => setIsHovered(true)}
3178
onMouseLeave={() => setIsHovered(false)}
3279
>
33-
{capitalize(type)!== 'Start' && (
80+
{ (
3481
<Handle
3582
type='target'
3683
position={targetPosition}
@@ -43,25 +90,27 @@ export default memo((props: any) => {
4390
data={data}
4491
onClick={() => onClick(data)}
4592
/>
46-
{capitalize(type) !== 'End' && (
93+
{(
4794
<Handle
4895
type='source'
4996
position={sourcePosition}
5097
isConnectable={isConnectable}
5198
>
5299
{(selected || isHovered) && (
53100
<div className='xflow-node-add-box'>
54-
<Tooltip
55-
title='点击添加节点'
56-
arrow={false}
57-
overlayInnerStyle={{
58-
background: '#fff',
59-
color: '#354052',
60-
fontSize: '12px'
61-
}}
62-
>
63-
<PlusOutlined style={{ color: '#fff', fontSize: 10 }}/>
64-
</Tooltip>
101+
<NodeSelectPopover placement='right' addNode={handleAddNode}>
102+
<Tooltip
103+
title='点击添加节点'
104+
arrow={false}
105+
overlayInnerStyle={{
106+
background: '#fff',
107+
color: '#354052',
108+
fontSize: '12px'
109+
}}
110+
>
111+
<PlusOutlined style={{ color: '#fff', fontSize: 10 }}/>
112+
</Tooltip>
113+
</NodeSelectPopover>
65114
</div>
66115
)}
67116
</Handle>

packages/x-flow/src/components/NodeSelectPopover/index.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import React, { useCallback, useState, useRef } from 'react';
33
import { Popover, Input, Tabs } from 'antd';
44
import { SearchOutlined } from '@ant-design/icons';
5-
import { useEventListener } from 'ahooks';
65
import { useShallow } from 'zustand/react/shallow';
76
import { useClickAway } from 'ahooks';
87
import { useSet } from '../../core/utils/hooks';
@@ -154,14 +153,14 @@ export default (props: any) => {
154153

155154
const handAddNode = useCallback<any>((ev: any, type: any) => {
156155
ev.stopPropagation(); // 阻止事件冒泡
157-
addNode({ node: type });
156+
addNode({ _nodeType: type });
158157
setOpen(false);
159158
}, []);
160159

161160
return (
162161
<Popover
163162
content={<SelectNodeView onCreate={handAddNode} nodeMenus={nodeMenus} containerRef={ref}/>}
164-
zIndex={1000}
163+
zIndex={2000}
165164
trigger='click'
166165
arrow={false}
167166
open={open}

0 commit comments

Comments
 (0)