代码迁移出去。

This commit is contained in:
戚辰先生 2024-12-06 22:19:06 +08:00
parent a45d25634f
commit 0ec831fc36
3 changed files with 343 additions and 349 deletions

View File

@ -232,7 +232,6 @@
"version": "2.1.6", "version": "2.1.6",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-clipboard/-/x6-plugin-clipboard-2.1.6.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-clipboard/-/x6-plugin-clipboard-2.1.6.tgz",
"integrity": "sha512-roZPLnZx6PK8MBvee0QMo90fz/TXeF0WNe4EGin2NBq5M1I5XTWrYvA6N2XVIiWAAI67gjQeEE8TpkL7f8QdqA==", "integrity": "sha512-roZPLnZx6PK8MBvee0QMo90fz/TXeF0WNe4EGin2NBq5M1I5XTWrYvA6N2XVIiWAAI67gjQeEE8TpkL7f8QdqA==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@antv/x6": "^2.x" "@antv/x6": "^2.x"
} }
@ -250,7 +249,6 @@
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-history/-/x6-plugin-history-2.2.4.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-history/-/x6-plugin-history-2.2.4.tgz",
"integrity": "sha512-9gHHvEW4Fla+1hxUV49zNgJyIMoV9CjVM52MrFgAJcvyRn1Kvxz4MfxiKlG+DEZUs+/zvfjl9pS6gJOd8laRkg==", "integrity": "sha512-9gHHvEW4Fla+1hxUV49zNgJyIMoV9CjVM52MrFgAJcvyRn1Kvxz4MfxiKlG+DEZUs+/zvfjl9pS6gJOd8laRkg==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@antv/x6": "^2.x" "@antv/x6": "^2.x"
} }
@ -259,7 +257,6 @@
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-keyboard/-/x6-plugin-keyboard-2.2.3.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-keyboard/-/x6-plugin-keyboard-2.2.3.tgz",
"integrity": "sha512-pnCIC+mDyKKfkcDyLePfGxKVIqXBcldTgannITkHC1kc0IafRS1GMvzpvuDGrM5haRYd6Nwz8kjkJyHkJE4GPA==", "integrity": "sha512-pnCIC+mDyKKfkcDyLePfGxKVIqXBcldTgannITkHC1kc0IafRS1GMvzpvuDGrM5haRYd6Nwz8kjkJyHkJE4GPA==",
"license": "MIT",
"dependencies": { "dependencies": {
"mousetrap": "^1.6.5" "mousetrap": "^1.6.5"
}, },
@ -271,7 +268,6 @@
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-minimap/-/x6-plugin-minimap-2.0.7.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-minimap/-/x6-plugin-minimap-2.0.7.tgz",
"integrity": "sha512-8zzESCx0jguFPCOKCA0gPFb6JmRgq81CXXtgJYe34XhySdZ6PB23I5Po062lNsEuTjTjnzli4lju8vvU+jzlGw==", "integrity": "sha512-8zzESCx0jguFPCOKCA0gPFb6JmRgq81CXXtgJYe34XhySdZ6PB23I5Po062lNsEuTjTjnzli4lju8vvU+jzlGw==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@antv/x6": "^2.x" "@antv/x6": "^2.x"
} }
@ -280,7 +276,6 @@
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-selection/-/x6-plugin-selection-2.2.2.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-selection/-/x6-plugin-selection-2.2.2.tgz",
"integrity": "sha512-s2gtR9Onlhr7HOHqyqg0d+4sG76JCcQEbvrZZ64XmSChlvieIPlC3YtH4dg1KMNhYIuBmBmpSum6S0eVTEiPQw==", "integrity": "sha512-s2gtR9Onlhr7HOHqyqg0d+4sG76JCcQEbvrZZ64XmSChlvieIPlC3YtH4dg1KMNhYIuBmBmpSum6S0eVTEiPQw==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@antv/x6": "^2.x" "@antv/x6": "^2.x"
} }
@ -289,7 +284,6 @@
"version": "2.1.7", "version": "2.1.7",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-snapline/-/x6-plugin-snapline-2.1.7.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-snapline/-/x6-plugin-snapline-2.1.7.tgz",
"integrity": "sha512-AsysoCb9vES0U2USNhEpYuO/W8I0aYfkhlbee5Kt4NYiMfQfZKQyqW/YjDVaS2pm38C1NKu1LdPVk/BBr4CasA==", "integrity": "sha512-AsysoCb9vES0U2USNhEpYuO/W8I0aYfkhlbee5Kt4NYiMfQfZKQyqW/YjDVaS2pm38C1NKu1LdPVk/BBr4CasA==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@antv/x6": "^2.x" "@antv/x6": "^2.x"
} }
@ -298,7 +292,6 @@
"version": "2.1.8", "version": "2.1.8",
"resolved": "https://registry.npmmirror.com/@antv/x6-plugin-transform/-/x6-plugin-transform-2.1.8.tgz", "resolved": "https://registry.npmmirror.com/@antv/x6-plugin-transform/-/x6-plugin-transform-2.1.8.tgz",
"integrity": "sha512-GvJuiJ4BKp0H7+qx3R1I+Vzbw5gXp9+oByXo/WyVxE3urOC7LC5sqnaDfIjyYMN6ROLPYPZraLSeSyYBgMgcDw==", "integrity": "sha512-GvJuiJ4BKp0H7+qx3R1I+Vzbw5gXp9+oByXo/WyVxE3urOC7LC5sqnaDfIjyYMN6ROLPYPZraLSeSyYBgMgcDw==",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@antv/x6": "^2.x" "@antv/x6": "^2.x"
} }

View File

@ -6,13 +6,6 @@ import {getDefinition, updateDefinition} from '../../service';
import {WorkflowDefinition, WorkflowStatus} from '../../../Workflow/types'; import {WorkflowDefinition, WorkflowStatus} from '../../../Workflow/types';
import {Graph, Node, Cell, Edge, Shape} from '@antv/x6'; import {Graph, Node, Cell, Edge, Shape} from '@antv/x6';
import '@antv/x6-react-shape'; import '@antv/x6-react-shape';
import {Selection} from '@antv/x6-plugin-selection';
import {History} from '@antv/x6-plugin-history';
import {Clipboard} from '@antv/x6-plugin-clipboard';
import {Transform} from '@antv/x6-plugin-transform';
import {Keyboard} from '@antv/x6-plugin-keyboard';
import {Snapline} from '@antv/x6-plugin-snapline';
import {MiniMap} from '@antv/x6-plugin-minimap';
import './index.module.less'; import './index.module.less';
import NodePanel from './components/NodePanel'; import NodePanel from './components/NodePanel';
import NodeConfig from './components/NodeConfig'; import NodeConfig from './components/NodeConfig';
@ -22,9 +15,9 @@ import {DeleteOutlined, CopyOutlined, SettingOutlined, ClearOutlined, Fullscreen
import EdgeConfig from './components/EdgeConfig'; import EdgeConfig from './components/EdgeConfig';
import { validateFlow, hasCycle } from './validate'; import { validateFlow, hasCycle } from './validate';
import { generateNodeStyle, generatePorts, calculateNodePosition, calculateCanvasPosition } from './utils/nodeUtils'; import { generateNodeStyle, generatePorts, calculateNodePosition, calculateCanvasPosition } from './utils/nodeUtils';
import { Position } from './types';
import { NODE_CONFIG } from './configs/nodeConfig'; import { NODE_CONFIG } from './configs/nodeConfig';
import { isWorkflowError } from './utils/errors'; import { isWorkflowError } from './utils/errors';
import { initGraph } from './utils/graphUtils';
const {Sider, Content} = Layout; const {Sider, Content} = Layout;
@ -194,264 +187,38 @@ const FlowDesigner: React.FC = () => {
}, []); }, []);
// 初始化图形 // 初始化图形
const initGraph = () => { useEffect(() => {
if (!containerRef.current) return; if (detail && containerRef.current) {
const graph = initGraph({
const graph = new Graph({
container: containerRef.current, container: containerRef.current,
grid: { miniMapContainer: document.getElementById('workflow-minimap')!,
visible: true, onContextMenu: (params) => setContextMenu(params),
type: 'mesh', onNodeClick: (node) => {
size: 10, setCurrentNode(node);
args: { const data = node.getData() as NodeData;
color: '#e5e5e5', console.log('Node clicked, data:', data);
thickness: 1,
}, if (data) {
}, const nodeType = nodeTypes.find(type => type.code === data.type);
mousewheel: { if (nodeType) {
enabled: true, setCurrentNodeType(nodeType);
modifiers: ['ctrl', 'meta'], const formValues = {
minScale: 0.5, name: data.name || nodeType.name,
maxScale: 2, description: data.description,
}, ...data.config
connecting: { };
snap: true, console.log('Setting form values:', formValues);
allowBlank: false, form.setFieldsValue(formValues);
allowLoop: false, setConfigVisible(true);
allowNode: false, } else {
allowEdge: false, message.error('未找到对应的节点类型');
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
router: {
name: 'manhattan',
args: {
padding: 1,
},
},
validateConnection({sourceCell, targetCell, sourceMagnet, targetMagnet}) {
if (sourceCell === targetCell) {
return false;
} }
if (!sourceMagnet || !targetMagnet) {
return false;
} }
return true;
}, },
}, onGraphChange: (g) => setGraph(g),
defaultEdge: { onDragOver: handleDragOver,
attrs: { onDrop: handleDrop,
line: { flowDetail: detail
stroke: '#5F95FF',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 8,
},
},
},
router: {
name: 'manhattan',
args: {
padding: 1,
},
},
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
labels: [
{
attrs: {
label: {
text: '',
fill: '#333',
fontSize: 12,
},
rect: {
fill: '#fff',
stroke: '#5F95FF',
strokeWidth: 1,
rx: 3,
ry: 3,
refWidth: 1,
refHeight: 1,
refX: 0,
refY: 0,
},
},
position: {
distance: 0.5,
offset: {
x: 0,
y: -10,
},
},
},
],
},
highlighting: {
magnetAvailable: {
name: 'stroke',
args: {
padding: 4,
attrs: {
strokeWidth: 4,
stroke: '#52c41a',
},
},
},
magnetAdsorbed: {
name: 'stroke',
args: {
padding: 4,
attrs: {
strokeWidth: 4,
stroke: '#1890ff',
},
},
},
},
keyboard: {
enabled: true,
},
clipboard: {
enabled: true,
},
history: {
enabled: true,
},
snapline: {
enabled: true,
},
translating: {
restrict: true,
},
background: {
color: '#ffffff', // 画布背景色
},
});
// 启用必要的功能
graph.use(
new Selection({
enabled: true,
multiple: true,
rubberband: true,
rubberEdge: true,
movable: true,
showNodeSelectionBox: true,
showEdgeSelectionBox: true,
selectCellOnMoved: false,
selectEdgeOnMoved: false,
selectNodeOnMoved: false,
className: 'node-selected',
strict: true,
})
);
graph.use(
new History({
enabled: true,
beforeAddCommand: (event: string, args: any) => {
if (event === 'cell:change:*') {
return true;
}
return true;
},
})
);
graph.use(
new Clipboard({
enabled: true,
})
);
graph.use(
new Transform({
resizing: {
enabled: true,
minWidth: 1,
minHeight: 1,
orthogonal: true,
restricted: true,
},
rotating: {
enabled: true,
grid: 15,
},
})
);
graph.use(
new Keyboard({
enabled: true,
})
);
graph.use(
new Snapline({
enabled: true,
})
);
// 启用小地图
graph.use(
new MiniMap({
container: document.getElementById('workflow-minimap'),
width: 200,
height: 150,
padding: 10,
scalable: false,
minScale: 0.5,
maxScale: 2,
graphOptions: {
async: true,
// 简化节点渲染
grid: false,
background: {
color: '#f5f5f5',
},
},
})
);
// 绑定右键菜单事件
graph.on('cell:contextmenu', ({ cell, e }) => {
e.preventDefault();
setContextMenu({
x: e.clientX,
y: e.clientY,
visible: true,
type: cell.isNode() ? 'node' : 'edge',
cell,
});
});
graph.on('blank:contextmenu', ({ e }) => {
e.preventDefault();
setContextMenu({
x: e.clientX,
y: e.clientY,
visible: true,
type: 'canvas',
});
});
// 点击画布时隐藏右键菜单
graph.on('blank:click', () => {
setContextMenu(prev => ({ ...prev, visible: false }));
});
// 点击节点时隐藏右键菜单
graph.on('cell:click', () => {
setContextMenu(prev => ({ ...prev, visible: false }));
}); });
graphRef.current = graph; graphRef.current = graph;
@ -461,45 +228,14 @@ const FlowDesigner: React.FC = () => {
if (detail) { if (detail) {
loadGraphData(graph, detail); loadGraphData(graph, detail);
} }
// 监听画布拖拽事件
containerRef.current.addEventListener('dragover', handleDragOver);
containerRef.current.addEventListener('drop', handleDrop);
// 监听节点点击事件
graph.on('node:click', ({node}: { node: Node }) => {
setCurrentNode(node);
const data = node.getData() as NodeData;
console.log('Node clicked, data:', data);
if (data) {
// 获取节点类型
const nodeType = nodeTypes.find(type => type.code === data.type);
if (nodeType) {
setCurrentNodeType(nodeType);
// 合并节点基本配置和执器配置
const formValues = {
name: data.name || nodeType.name,
description: data.description,
...data.config // 直接展开所有配置
};
console.log('Setting form values:', formValues);
form.setFieldsValue(formValues);
setConfigVisible(true);
} else {
message.error('未找到对应的节点类型');
} }
}
});
// 监听选择状态变化 return () => {
graph.on('selection:changed', () => { if (graphRef.current) {
// 强制更新工具栏状态 graphRef.current.dispose();
setGraph(graph); }
});
}; };
}, [detail]);
// 处理拖拽移动 // 处理拖拽移动
const handleDragOver = (e: DragEvent) => { const handleDragOver = (e: DragEvent) => {
@ -714,43 +450,6 @@ const FlowDesigner: React.FC = () => {
fetchDetail(); fetchDetail();
}, [id]); }, [id]);
// 初始化图形
useEffect(() => {
if (detail && containerRef.current) {
initGraph();
}
// 清理事件监听
return () => {
if (containerRef.current) {
containerRef.current.removeEventListener('dragover', handleDragOver);
containerRef.current.removeEventListener('drop', handleDrop);
}
if (graphRef.current) {
graphRef.current.dispose();
}
};
}, [detail, containerRef.current]);
useEffect(() => {
if (!containerRef.current) return;
const resizeObserver = new ResizeObserver(() => {
if (graphRef.current) {
const container = containerRef.current;
if (container) {
const { width, height } = container.getBoundingClientRect();
graphRef.current.resize(width, height);
}
}
});
resizeObserver.observe(containerRef.current);
return () => {
resizeObserver.disconnect();
};
}, []);
// 处理节点拖拽开始 // 处理节点拖拽开始
const handleNodeDragStart = (nodeType: NodeType) => { const handleNodeDragStart = (nodeType: NodeType) => {
draggedNodeRef.current = nodeType; draggedNodeRef.current = nodeType;

View File

@ -0,0 +1,302 @@
import { Graph } from '@antv/x6';
import { Selection } from '@antv/x6-plugin-selection';
import { Keyboard } from '@antv/x6-plugin-keyboard';
import { Clipboard } from '@antv/x6-plugin-clipboard';
import { History } from '@antv/x6-plugin-history';
import { Transform } from '@antv/x6-plugin-transform';
import { Snapline } from '@antv/x6-plugin-snapline';
import { MiniMap } from '@antv/x6-plugin-minimap';
import { NodeData, NodeType, WorkflowDefinition } from '../types';
import { Node } from '@antv/x6';
export const initGraph = ({
container,
miniMapContainer,
onContextMenu,
onNodeClick,
onGraphChange,
onDragOver,
onDrop,
flowDetail
}: {
container: HTMLDivElement;
miniMapContainer: HTMLElement;
onContextMenu: (params: any) => void;
onNodeClick: (node: Node) => void;
onGraphChange: (graph: Graph) => void;
onDragOver: (e: DragEvent) => void;
onDrop: (e: DragEvent) => void;
flowDetail?: WorkflowDefinition;
}) => {
const graph = new Graph({
container,
grid: {
visible: true,
type: 'mesh',
size: 10,
args: {
color: '#e5e5e5',
thickness: 1,
},
},
mousewheel: {
enabled: true,
modifiers: ['ctrl', 'meta'],
minScale: 0.5,
maxScale: 2,
},
connecting: {
snap: true,
allowBlank: false,
allowLoop: false,
allowNode: false,
allowEdge: false,
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
router: {
name: 'manhattan',
args: {
padding: 1,
},
},
validateConnection({sourceCell, targetCell, sourceMagnet, targetMagnet}) {
if (sourceCell === targetCell) {
return false;
}
if (!sourceMagnet || !targetMagnet) {
return false;
}
return true;
},
},
defaultEdge: {
attrs: {
line: {
stroke: '#5F95FF',
strokeWidth: 1,
targetMarker: {
name: 'classic',
size: 8,
},
},
},
router: {
name: 'manhattan',
args: {
padding: 1,
},
},
connector: {
name: 'rounded',
args: {
radius: 8,
},
},
labels: [
{
attrs: {
label: {
text: '',
fill: '#333',
fontSize: 12,
},
rect: {
fill: '#fff',
stroke: '#5F95FF',
strokeWidth: 1,
rx: 3,
ry: 3,
refWidth: 1,
refHeight: 1,
refX: 0,
refY: 0,
},
},
position: {
distance: 0.5,
offset: {
x: 0,
y: -10,
},
},
},
],
},
highlighting: {
magnetAvailable: {
name: 'stroke',
args: {
padding: 4,
attrs: {
strokeWidth: 4,
stroke: '#52c41a',
},
},
},
magnetAdsorbed: {
name: 'stroke',
args: {
padding: 4,
attrs: {
strokeWidth: 4,
stroke: '#1890ff',
},
},
},
},
keyboard: {
enabled: true,
},
clipboard: {
enabled: true,
},
history: {
enabled: true,
},
snapline: {
enabled: true,
},
translating: {
restrict: true,
},
background: {
color: '#ffffff', // 画布背景色
},
});
// 启用必要的功能
graph.use(
new Selection({
enabled: true,
multiple: true,
rubberband: true,
rubberEdge: true,
movable: true,
showNodeSelectionBox: true,
showEdgeSelectionBox: true,
selectCellOnMoved: false,
selectEdgeOnMoved: false,
selectNodeOnMoved: false,
className: 'node-selected',
strict: true,
})
);
graph.use(
new History({
enabled: true,
beforeAddCommand: (event: string, args: any) => {
if (event === 'cell:change:*') {
return true;
}
return true;
},
})
);
graph.use(
new Clipboard({
enabled: true,
})
);
graph.use(
new Transform({
resizing: {
enabled: true,
minWidth: 1,
minHeight: 1,
orthogonal: true,
restricted: true,
},
rotating: {
enabled: true,
grid: 15,
},
})
);
graph.use(
new Keyboard({
enabled: true,
})
);
graph.use(
new Snapline({
enabled: true,
})
);
// 启用小地图
graph.use(
new MiniMap({
container: miniMapContainer,
width: 200,
height: 150,
padding: 10,
scalable: false,
minScale: 0.5,
maxScale: 2,
graphOptions: {
async: true,
grid: false,
background: {
color: '#f5f5f5',
},
},
})
);
// 绑定事件
graph.on('cell:contextmenu', ({ cell, e }) => {
e.preventDefault();
onContextMenu({
x: e.clientX,
y: e.clientY,
visible: true,
type: cell.isNode() ? 'node' : 'edge',
cell,
});
});
graph.on('blank:contextmenu', ({ e }) => {
e.preventDefault();
onContextMenu({
x: e.clientX,
y: e.clientY,
visible: true,
type: 'canvas',
});
});
// 点击画布时隐藏右键菜单
graph.on('blank:click', () => {
onContextMenu({ visible: false, x: 0, y: 0, type: 'canvas' });
});
// 点击节点时隐藏右键菜单
graph.on('cell:click', () => {
onContextMenu({ visible: false, x: 0, y: 0, type: 'canvas' });
});
// 监听节点点击事件
graph.on('node:click', ({ node }) => {
onNodeClick(node);
});
// 监听选择状态变化
graph.on('selection:changed', () => {
onGraphChange(graph);
});
// 监听画布拖拽事件
container.addEventListener('dragover', onDragOver);
container.addEventListener('drop', onDrop);
return graph;
};