Skip to content

Commit 65f99d4

Browse files
committed
feat:粘贴到某一个出口
1 parent 61b7e65 commit 65f99d4

File tree

4 files changed

+113
-24
lines changed

4 files changed

+113
-24
lines changed

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

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { MoreOutlined } from '@ant-design/icons';
22
import { Handle, Position, useReactFlow } from '@xyflow/react';
33
import { Dropdown, message } from "antd";
44

5+
import { ItemType } from 'antd/es/menu/interface';
56
import classNames from 'classnames';
6-
import React, { memo, useCallback, useContext, useState } from 'react';
7+
import { isFunction } from 'lodash';
8+
import React, { memo, useCallback, useContext, useMemo, useState } from 'react';
79
import { shallow } from 'zustand/shallow';
810
import { useStore } from '../../hooks/useStore';
911
import { ConfigContext } from '../../models/context';
@@ -14,8 +16,10 @@ import SourceHandle from './sourceHandle';
1416
export default memo((props: any) => {
1517
const { id, type, data, layout, isConnectable, selected, onClick, status } =
1618
props;
17-
const { widgets, settingMap, globalConfig } = useContext(ConfigContext);
19+
const { widgets, settingMap, globalConfig, onMenuItemClick } =
20+
useContext(ConfigContext);
1821
const deletable = globalConfig?.edge?.deletable ?? true;
22+
1923
const NodeWidget =
2024
widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];
2125
const [isHovered, setIsHovered] = useState(false);
@@ -50,7 +54,7 @@ export default memo((props: any) => {
5054
type: 'custom',
5155
data: {
5256
...data,
53-
title: `${title}_${uuid4()}`
57+
title: `${title}_${uuid4()}`,
5458
},
5559
position: { x, y },
5660
};
@@ -77,14 +81,92 @@ export default memo((props: any) => {
7781
message.success('复制成功');
7882
}, [copyNode]);
7983

80-
const handlePasteNode = useCallback(() => {
81-
pasteNode(id)
82-
}, [pasteNode]);
84+
const handlePasteNode = useCallback(
85+
(data?: { sourceHandle: string }) => {
86+
pasteNode(id, data);
87+
},
88+
[pasteNode]
89+
);
8390

8491
const handleDeleteNode = useCallback(() => {
85-
deleteNode(id)
92+
deleteNode(id);
8693
}, [pasteNode]);
8794

95+
const itemClick = e => {
96+
if (!e.key) {
97+
return;
98+
}
99+
const sourceHandle = e.item.props?.sourcehandle;
100+
if (isFunction(onMenuItemClick)) {
101+
const data: Record<string, string> = {
102+
key: e.key,
103+
nodeId: id,
104+
};
105+
if (type === 'Switch' && e.key.startsWith('paste-') && sourceHandle) {
106+
data['sourceHandle'] = sourceHandle;
107+
}
108+
onMenuItemClick(data);
109+
} else {
110+
if (e.key === 'copy') {
111+
handleCopyNode();
112+
} else if (e.key === 'paste') {
113+
handlePasteNode();
114+
} else if (e.key === 'delete') {
115+
handleDeleteNode();
116+
} else if (e.key.startsWith('paste-')) {
117+
if (sourceHandle) {
118+
handlePasteNode({
119+
sourceHandle,
120+
});
121+
} else {
122+
handlePasteNode();
123+
}
124+
}
125+
}
126+
};
127+
128+
const menuItem: ItemType[] = useMemo(() => {
129+
if (type === 'Switch') {
130+
let list = [];
131+
if (Array.isArray(data.list)) {
132+
const len = data.list.length;
133+
list = data.list.map((r, i) => {
134+
if (i === 0) {
135+
return {
136+
label: `粘贴到第${i + 1}个出口`,
137+
key: 'paste-' + i,
138+
index: i,
139+
id: id,
140+
};
141+
} else {
142+
return {
143+
label: `粘贴到第${i + 1}个出口`,
144+
key: 'paste-' + i,
145+
id: id,
146+
index: i,
147+
sourcehandle: r._conditionId,
148+
};
149+
}
150+
});
151+
}
152+
return [
153+
...list,
154+
{
155+
label: `粘贴到第${list.length + 1}个出口`,
156+
key: 'paste-' + (list.length + 1),
157+
id: id,
158+
index: list.length + 1,
159+
sourcehandle: 'condition_else',
160+
},
161+
];
162+
}
163+
return [
164+
{
165+
label: '粘贴',
166+
key: 'paste',
167+
},
168+
];
169+
}, [type]);
88170

89171
// 节点状态处理
90172
const statusObj = transformNodeStatus(globalConfig?.nodeView?.status || []);
@@ -113,29 +195,24 @@ export default memo((props: any) => {
113195
items: [
114196
{
115197
label: '复制',
116-
key: ' copy',
117-
onClick: handleCopyNode,
118-
},
119-
{
120-
label: '粘贴',
121-
key: 'paste',
122-
onClick: handlePasteNode,
198+
key: 'copy',
123199
},
200+
...menuItem,
124201
{
125202
label: '删除',
126203
key: 'delete',
127204
danger: true,
128-
onClick: handleDeleteNode,
129205
},
130206
],
207+
onClick: itemClick,
131208
}}
132209
trigger={['click', 'contextMenu']}
133210
>
134211
<div className="xflow-node-actions-container">
135-
<MoreOutlined
136-
style={{ transform: 'rotateZ(90deg)', fontSize: '20px' }}
137-
></MoreOutlined>
138-
</div>
212+
<MoreOutlined
213+
style={{ transform: 'rotateZ(90deg)', fontSize: '20px' }}
214+
></MoreOutlined>
215+
</div>
139216
</Dropdown>
140217
)}
141218
<NodeWidget

packages/x-flow/src/models/store.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { message } from 'antd';
12
import { generateCopyNodes, transformNodes, uuid } from '../utils';
23
import {
34
addEdge,
@@ -43,7 +44,7 @@ export type FlowState = {
4344
addEdges: (edges: Edge[] | Edge) => void;
4445
deleteNode: (nodeId: string) => void;
4546
copyNode: (nodeId: string) => void;
46-
pasteNode: (nodeId: string) => void;
47+
pasteNode: (nodeId: string,data?:Record<string,string>) => void;
4748
setLayout: (layout: 'LR' | 'TB') => void;
4849
setIsAddingNode: (payload: boolean) => void;
4950
setCandidateNode: (candidateNode: any) => void;
@@ -129,18 +130,21 @@ const createStore = (initProps?: Partial<FlowProps>) => {
129130
copyNodes,
130131
});
131132
},
132-
pasteNode: (nodeId) => {
133+
pasteNode: (nodeId,data = {}) => {
133134
if (get().copyNodes.length > 0) {
134135
const newEdges = {
135136
id: uuid(),
136137
source: nodeId,
137138
target: get().copyNodes[0].id,
139+
...data
138140
};
139141
get().addNodes(get().copyNodes, false);
140142
get().addEdges(newEdges);
141143
set({
142144
copyNodes: [],
143145
});
146+
}else{
147+
message.warning('请先复制节点!')
144148
}
145149
},
146150
deleteNode: (nodeId) => {

packages/x-flow/src/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ export interface TNodeView {
7373
export interface TEdge {
7474
// 边的配置
7575
hideEdgeAddBtn?: boolean; // 是否隐藏两个节点之间,连线上的增加节点按钮
76-
hideEdgeDelBtn?:boolean; // 是否隐藏两个节点之间,连线上的删除节点按钮
77-
deletable?:boolean // 是否允许删除线条 初始化的edges不受此项影响
76+
hideEdgeDelBtn?: boolean; // 是否隐藏两个节点之间,连线上的删除节点按钮
77+
deletable?: boolean; // 是否允许删除线条 初始化的edges不受此项影响
7878
}
7979

8080
export interface FlowProps {
@@ -100,6 +100,12 @@ export interface FlowProps {
100100
};
101101
logPanel?: TLogPanel; // 日志面板配置
102102
onNodeClick?: NodeMouseHandler;
103+
onMenuItemClick: (itemInfo: ItemInfo) => void;
104+
}
105+
interface ItemInfo {
106+
key: 'copy' | 'paste' | 'delete' | string;
107+
nodeId: string;
108+
sourceHandle?: string;
103109
}
104110

105111
export default FlowProps;

packages/x-flow/src/withProvider.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export default function withProvider<T>(
2929
iconFontUrl,
3030
globalConfig,
3131
logPanel,
32-
antdVersion,
32+
onMenuItemClick,
33+
antdVersion = 'V5',
3334
...restProps
3435
} = props;
3536
const settingMap = useMemo(() => {
@@ -55,6 +56,7 @@ export default function withProvider<T>(
5556
globalConfig,
5657
logPanel,
5758
antdVersion,
59+
onMenuItemClick,
5860
widgets: {
5961
...defaultWidgets,
6062
...widgets,

0 commit comments

Comments
 (0)