代码迁移出去。
This commit is contained in:
parent
a45d25634f
commit
0ec831fc36
7
frontend/package-lock.json
generated
7
frontend/package-lock.json
generated
@ -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"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user