Skip to content

Commit 8ba0943

Browse files
author
昔梦
committed
Merge branch 'xflow' of https://github.com/alibaba/x-render into xflow
2 parents c890e46 + 8721e1e commit 8ba0943

File tree

5 files changed

+129
-20
lines changed

5 files changed

+129
-20
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
.xflow-node-container {
22
border: 2px solid var(--nodeBorderColor,#fff);
33
border-radius: 14px;
4-
4+
position: relative;
55
.react-flow__edge-path,
66
.react-flow__connection-path {
77
stroke: #d0d5dc;
88
stroke-width: 2px;
99
}
10-
10+
.xflow-node-actions-container {
11+
cursor: pointer;
12+
position: absolute;
13+
top: -4px;
14+
background: #ffffff;
15+
transform: translate(0, -100%);
16+
right: 6px;
17+
padding: 0 2px;
18+
border-radius: 4px;
19+
box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, .05);
20+
}
1121
.react-flow__handle {
1222
width: 30px;
1323
height: 30px;

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

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
import { MoreOutlined } from '@ant-design/icons';
12
import { Handle, Position, useReactFlow } from '@xyflow/react';
3+
import { Dropdown, message } from "antd";
4+
25
import classNames from 'classnames';
3-
import React, { memo, useContext, useState } from 'react';
6+
import React, { memo, useCallback, useContext, useState } from 'react';
47
import { shallow } from 'zustand/shallow';
58
import { useStore } from '../../hooks/useStore';
69
import { ConfigContext } from '../../models/context';
@@ -16,13 +19,16 @@ export default memo((props: any) => {
1619
widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];
1720
const [isHovered, setIsHovered] = useState(false);
1821
const reactflow = useReactFlow();
19-
const { addNodes, addEdges, mousePosition } = useStore(
22+
const { addNodes, addEdges, copyNode, pasteNode, deleteNode, mousePosition } = useStore(
2023
(state: any) => ({
2124
nodes: state.nodes,
2225
edges: state.edges,
2326
mousePosition: state.mousePosition,
2427
addNodes: state.addNodes,
2528
addEdges: state.addEdges,
29+
copyNode: state.copyNode,
30+
pasteNode: state.pasteNode,
31+
deleteNode: state.deleteNode,
2632
onEdgesChange: state.onEdgesChange,
2733
}),
2834
shallow
@@ -43,7 +49,7 @@ export default memo((props: any) => {
4349
type: 'custom',
4450
data: {
4551
...data,
46-
title: `${title}_${uuid4()}`,
52+
title: `${title}_${uuid4()}`
4753
},
4854
position: { x, y },
4955
};
@@ -64,6 +70,20 @@ export default memo((props: any) => {
6470
sourcePosition = Position.Bottom;
6571
}
6672

73+
const handleCopyNode = useCallback(() => {
74+
copyNode(id);
75+
message.success('复制成功');
76+
}, [copyNode]);
77+
78+
const handlePasteNode = useCallback(() => {
79+
pasteNode(id)
80+
}, [pasteNode]);
81+
82+
const handleDeleteNode = useCallback(() => {
83+
deleteNode(id)
84+
}, [pasteNode]);
85+
86+
6787
// 节点状态处理
6888
const statusObj = transformNodeStatus(globalConfig?.nodeView?.status || []);
6989
const nodeBorderColor = statusObj[status]?.color;
@@ -85,6 +105,37 @@ export default memo((props: any) => {
85105
isConnectable={isConnectable}
86106
/>
87107
)}
108+
{selected && (
109+
<Dropdown
110+
menu={{
111+
items: [
112+
{
113+
label: '复制',
114+
key: ' copy',
115+
onClick: handleCopyNode,
116+
},
117+
{
118+
label: '粘贴',
119+
key: 'paste',
120+
onClick: handlePasteNode,
121+
},
122+
{
123+
label: '删除',
124+
key: 'delete',
125+
danger: true,
126+
onClick: handleDeleteNode,
127+
},
128+
],
129+
}}
130+
trigger={['click', 'contextMenu']}
131+
>
132+
<div className="xflow-node-actions-container">
133+
<MoreOutlined
134+
style={{ transform: 'rotateZ(90deg)', fontSize: '20px' }}
135+
></MoreOutlined>
136+
</div>
137+
</Dropdown>
138+
)}
88139
<NodeWidget
89140
id={id}
90141
type={type}

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { generateCopyNodes, uuid } from '../utils';
12
import {
23
addEdge,
34
applyEdgeChanges,
@@ -27,6 +28,8 @@ export type FlowState = {
2728
layout?: 'LR' | 'TB';
2829
nodes?: FlowNode[];
2930
edges?: Edge[];
31+
copyNodes: FlowNode[];
32+
copyEdges: Edge[];
3033
panOnDrag?: boolean;
3134
isAddingNode?: boolean;
3235
candidateNode: any;
@@ -36,8 +39,11 @@ export type FlowState = {
3639
onConnect: OnConnect;
3740
setNodes: (nodes: FlowNode[]) => void;
3841
setEdges: (edges: Edge[]) => void;
39-
addNodes: (nodes: FlowNode[]) => void;
40-
addEdges: (edges: Edge[]) => void;
42+
addNodes: (nodes: FlowNode[]| FlowNode) => void;
43+
addEdges: (edges: Edge[] | Edge) => void;
44+
deleteNode: (nodeId: string) => void;
45+
copyNode: (nodeId: string) => void;
46+
pasteNode: (nodeId: string) => void;
4147
setLayout: (layout: 'LR' | 'TB') => void;
4248
setIsAddingNode: (payload: boolean) => void;
4349
setCandidateNode: (candidateNode: any) => void;
@@ -57,6 +63,8 @@ const createStore = (initProps?: Partial<FlowProps>) => {
5763
(set, get) => ({
5864
...DEFAULT_PROPS,
5965
...initProps,
66+
copyNodes: [],
67+
copyEdges: [],
6068
isAddingNode: false,
6169
candidateNode: null,
6270
// nodeMenus: [],
@@ -113,6 +121,33 @@ const createStore = (initProps?: Partial<FlowProps>) => {
113121
}
114122
set({ layout });
115123
},
124+
copyNode: (nodeId) => {
125+
const copyNodes = generateCopyNodes(
126+
get().nodes.find((node) => node.id === nodeId),
127+
);
128+
set({
129+
copyNodes,
130+
});
131+
},
132+
pasteNode: (nodeId) => {
133+
if (get().copyNodes.length > 0) {
134+
const newEdges = {
135+
id: uuid(),
136+
source: nodeId,
137+
target: get().copyNodes[0].id,
138+
};
139+
get().addNodes(get().copyNodes);
140+
get().addEdges(newEdges);
141+
set({
142+
copyNodes: [],
143+
});
144+
}
145+
},
146+
deleteNode: (nodeId) => {
147+
set({
148+
nodes: get().nodes.filter((node) => node.id !== nodeId),
149+
});
150+
},
116151
}),
117152
{
118153
// nodes 和 edges 是引用类型,所以使用深比较

packages/x-flow/src/utils/flow.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { uuid } from './';
2+
3+
/**
4+
* 获取所有子节点
5+
*/
6+
export const generateCopyNodes = (parentNode: any) => {
7+
// 1、定义 childNodeIds 数组,用于存储找到的所有节点的 id,默认把 rootNode 添加到数组中
8+
const childNodes: any[] = [];
9+
const rootNode = {
10+
id: uuid(),
11+
type: parentNode.type,
12+
data: {
13+
...parentNode.data,
14+
},
15+
position: { x: 0, y: 0 },
16+
sourceId: parentNode.id,
17+
};
18+
childNodes.push(rootNode);
19+
20+
return childNodes;
21+
};

packages/x-flow/src/utils/index.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
1-
import { version as antdVersion } from 'antd';
1+
// import { version as antdVersion } from 'antd';
22
import { customAlphabet } from 'nanoid';
33
import tinycolor from 'tinycolor2';
44
export const uuid = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 16);
55
export const uuid4 = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 4);
66

7-
import {
8-
has as _has,
9-
cloneDeep,
10-
get,
11-
isMatch,
12-
isUndefined,
13-
merge,
14-
mergeWith,
15-
omitBy,
16-
set,
17-
some,
18-
} from 'lodash-es';
7+
import { isMatch, some, set, get, cloneDeep, has as _has, merge, mergeWith, isUndefined, omitBy } from 'lodash-es';
198

9+
const antdVersion = "5.22.6"
2010
export const _set = set;
2111
export const _get = get;
2212
export const _cloneDeep = cloneDeep;
@@ -233,6 +223,8 @@ export function safeJsonStringify(obj: Object) {
233223
}
234224
}
235225

226+
export * from './flow'
227+
236228
// 内置节点状态
237229
export const NODE_STATUS = {
238230
success: {

0 commit comments

Comments
 (0)