1
This commit is contained in:
parent
f1583c8b7b
commit
c2a460d7d1
@ -21,6 +21,7 @@ import '@antv/x6-plugin-history';
|
|||||||
import { Selection } from '@antv/x6-plugin-selection';
|
import { Selection } from '@antv/x6-plugin-selection';
|
||||||
import { MiniMap } from '@antv/x6-plugin-minimap';
|
import { MiniMap } from '@antv/x6-plugin-minimap';
|
||||||
import { Clipboard } from '@antv/x6-plugin-clipboard';
|
import { Clipboard } from '@antv/x6-plugin-clipboard';
|
||||||
|
import { History } from '@antv/x6-plugin-history';
|
||||||
import {getDefinitionDetail, saveDefinition} from '../service';
|
import {getDefinitionDetail, saveDefinition} from '../service';
|
||||||
import {getNodeDefinitionList} from './service';
|
import {getNodeDefinitionList} from './service';
|
||||||
import NodePanel from './components/NodePanel';
|
import NodePanel from './components/NodePanel';
|
||||||
@ -51,6 +52,107 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
const [nodeDefinitions, setNodeDefinitions] = useState<NodeDefinition[]>([]);
|
const [nodeDefinitions, setNodeDefinitions] = useState<NodeDefinition[]>([]);
|
||||||
const [isNodeDefinitionsLoaded, setIsNodeDefinitionsLoaded] = useState(false);
|
const [isNodeDefinitionsLoaded, setIsNodeDefinitionsLoaded] = useState(false);
|
||||||
|
|
||||||
|
// 初始化图形
|
||||||
|
const initGraph = () => {
|
||||||
|
if (!graphContainerRef.current) return null;
|
||||||
|
|
||||||
|
const graph = new Graph({
|
||||||
|
container: graphContainerRef.current,
|
||||||
|
grid: GRID_CONFIG,
|
||||||
|
connecting: CONNECTING_CONFIG,
|
||||||
|
highlighting: HIGHLIGHTING_CONFIG,
|
||||||
|
clipboard: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
selecting: {
|
||||||
|
enabled: true,
|
||||||
|
multiple: true,
|
||||||
|
rubberband: true,
|
||||||
|
movable: true,
|
||||||
|
showNodeSelectionBox: true,
|
||||||
|
},
|
||||||
|
snapline: true,
|
||||||
|
keyboard: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
mousewheel: {
|
||||||
|
enabled: true,
|
||||||
|
modifiers: ['ctrl', 'meta'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 注册插件
|
||||||
|
const history = new History({
|
||||||
|
enabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.use(new Selection());
|
||||||
|
graph.use(new MiniMap({
|
||||||
|
container: minimapContainerRef.current!,
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
}));
|
||||||
|
graph.use(new Clipboard());
|
||||||
|
graph.use(history);
|
||||||
|
|
||||||
|
// 扩展 graph 对象,添加 history 属性
|
||||||
|
(graph as any).history = history;
|
||||||
|
|
||||||
|
// 监听图形变化
|
||||||
|
graph.on('cell:added', ({ cell }) => {
|
||||||
|
const canUndo = history.canUndo();
|
||||||
|
console.log('Cell added, history:', canUndo, cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.on('cell:removed', ({ cell }) => {
|
||||||
|
const canUndo = history.canUndo();
|
||||||
|
console.log('Cell removed, history:', canUndo, cell);
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.on('cell:changed', ({ cell, options }) => {
|
||||||
|
const canUndo = history.canUndo();
|
||||||
|
console.log('Cell changed, history:', canUndo, cell, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
registerEventHandlers(graph);
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理撤销操作
|
||||||
|
const handleUndo = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
const history = (graph as any).history;
|
||||||
|
if (!history) {
|
||||||
|
console.error('History plugin not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('Can undo:', history.canUndo());
|
||||||
|
if (history.canUndo()) {
|
||||||
|
history.undo();
|
||||||
|
message.success('已撤销');
|
||||||
|
} else {
|
||||||
|
message.info('没有可撤销的操作');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理重做操作
|
||||||
|
const handleRedo = () => {
|
||||||
|
if (!graph) return;
|
||||||
|
const history = (graph as any).history;
|
||||||
|
if (!history) {
|
||||||
|
console.error('History plugin not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('Can redo:', history.canRedo());
|
||||||
|
if (history.canRedo()) {
|
||||||
|
history.redo();
|
||||||
|
message.success('已重做');
|
||||||
|
} else {
|
||||||
|
message.info('没有可重做的操作');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 注册事件处理器
|
// 注册事件处理器
|
||||||
const registerEventHandlers = (graph: Graph) => {
|
const registerEventHandlers = (graph: Graph) => {
|
||||||
// 显示/隐藏连接桩
|
// 显示/隐藏连接桩
|
||||||
@ -131,7 +233,9 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: '确定要删除该节点吗?',
|
content: '确定要删除该节点吗?',
|
||||||
onOk: () => cell.remove()
|
onOk: () => {
|
||||||
|
cell.remove();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,99 +309,18 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
loadNodeDefinitions();
|
loadNodeDefinitions();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 等待节点定义加载完成后再初始化图形
|
// 加载工作流定义详情
|
||||||
useEffect(() => {
|
|
||||||
if (!isNodeDefinitionsLoaded || !graphContainerRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const graph = new Graph({
|
|
||||||
container: graphContainerRef.current,
|
|
||||||
grid: GRID_CONFIG,
|
|
||||||
connecting: CONNECTING_CONFIG,
|
|
||||||
highlighting: HIGHLIGHTING_CONFIG,
|
|
||||||
snapline: true,
|
|
||||||
history: true,
|
|
||||||
clipboard: true,
|
|
||||||
keyboard: true,
|
|
||||||
background: {
|
|
||||||
color: '#f5f5f5',
|
|
||||||
},
|
|
||||||
width: graphContainerRef.current.clientWidth,
|
|
||||||
height: graphContainerRef.current.clientHeight,
|
|
||||||
});
|
|
||||||
|
|
||||||
// 注册选择插件
|
|
||||||
graph.use(
|
|
||||||
new Selection({
|
|
||||||
enabled: true,
|
|
||||||
multiple: true,
|
|
||||||
rubberband: true,
|
|
||||||
showNodeSelectionBox: true,
|
|
||||||
showEdgeSelectionBox: true,
|
|
||||||
className: 'node-selected',
|
|
||||||
pointerEvents: 'none',
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// 注册小地图插件
|
|
||||||
if (minimapContainerRef.current) {
|
|
||||||
graph.use(
|
|
||||||
new MiniMap({
|
|
||||||
container: minimapContainerRef.current,
|
|
||||||
width: 200,
|
|
||||||
height: 150,
|
|
||||||
padding: 10,
|
|
||||||
scalable: true,
|
|
||||||
minScale: 0.01,
|
|
||||||
maxScale: 16,
|
|
||||||
graphOptions: {
|
|
||||||
async: true,
|
|
||||||
getCellView(cell) {
|
|
||||||
if (cell.isNode()) {
|
|
||||||
return graph.findViewByCell(cell);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
createCellView(cell) {
|
|
||||||
if (cell.isEdge()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 注册剪贴板插件
|
|
||||||
graph.use(new Clipboard({
|
|
||||||
enabled: true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
registerEventHandlers(graph);
|
|
||||||
|
|
||||||
setGraph(graph);
|
|
||||||
|
|
||||||
// 在 graph 初始化完成后加载数据
|
|
||||||
if (id) {
|
|
||||||
loadDefinitionDetail(graph, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
graph.dispose();
|
|
||||||
};
|
|
||||||
}, [graphContainerRef, id, nodeDefinitions, isNodeDefinitionsLoaded]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!graph || !minimapContainerRef.current) return;
|
|
||||||
|
|
||||||
}, [graph]);
|
|
||||||
|
|
||||||
const loadDefinitionDetail = async (graphInstance: Graph, definitionId: string) => {
|
const loadDefinitionDetail = async (graphInstance: Graph, definitionId: string) => {
|
||||||
try {
|
try {
|
||||||
const response = await getDefinitionDetail(Number(definitionId));
|
const response = await getDefinitionDetail(Number(definitionId));
|
||||||
setTitle(`工作流设计 - ${response.name}`);
|
setTitle(`工作流设计 - ${response.name}`);
|
||||||
setDefinitionData(response);
|
setDefinitionData(response);
|
||||||
|
|
||||||
|
if (!graphInstance) {
|
||||||
|
console.error('Graph instance is not initialized');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 清空画布
|
// 清空画布
|
||||||
graphInstance.clearCells();
|
graphInstance.clearCells();
|
||||||
const nodeMap = new Map();
|
const nodeMap = new Map();
|
||||||
@ -334,10 +357,33 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 初始化图形和加载数据
|
||||||
|
useEffect(() => {
|
||||||
|
if (!graphContainerRef.current || !isNodeDefinitionsLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newGraph = initGraph();
|
||||||
|
if (newGraph) {
|
||||||
|
setGraph(newGraph);
|
||||||
|
|
||||||
|
// 在图形初始化完成后加载数据
|
||||||
|
if (id) {
|
||||||
|
loadDefinitionDetail(newGraph, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
graph?.dispose();
|
||||||
|
};
|
||||||
|
}, [graphContainerRef, id, nodeDefinitions, isNodeDefinitionsLoaded]);
|
||||||
|
|
||||||
|
// 处理节点拖拽开始
|
||||||
const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => {
|
const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => {
|
||||||
e.dataTransfer.setData('node', JSON.stringify(node));
|
e.dataTransfer.setData('node', JSON.stringify(node));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理节点拖拽结束
|
||||||
const handleDrop = (e: React.DragEvent) => {
|
const handleDrop = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!graph) return;
|
if (!graph) return;
|
||||||
@ -356,10 +402,12 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理拖拽过程中
|
||||||
const handleDragOver = (e: React.DragEvent) => {
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理节点配置更新
|
||||||
const handleNodeConfigUpdate = (values: any) => {
|
const handleNodeConfigUpdate = (values: any) => {
|
||||||
if (!selectedNode) return;
|
if (!selectedNode) return;
|
||||||
// 更新节点配置
|
// 更新节点配置
|
||||||
@ -373,6 +421,7 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
message.success('节点配置已更新');
|
message.success('节点配置已更新');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理保存工作流
|
||||||
const handleSaveWorkflow = async () => {
|
const handleSaveWorkflow = async () => {
|
||||||
if (!graph || !definitionData) return;
|
if (!graph || !definitionData) return;
|
||||||
|
|
||||||
@ -476,14 +525,20 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
<Tooltip title="撤销">
|
<Tooltip title="撤销">
|
||||||
<Button
|
<Button
|
||||||
icon={<UndoOutlined />}
|
icon={<UndoOutlined />}
|
||||||
onClick={() => graph?.undo()}
|
onClick={handleUndo}
|
||||||
/>
|
disabled={!(graph as any)?.history?.canUndo()}
|
||||||
|
>
|
||||||
|
撤销
|
||||||
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title="重做">
|
<Tooltip title="重做">
|
||||||
<Button
|
<Button
|
||||||
icon={<RedoOutlined />}
|
icon={<RedoOutlined />}
|
||||||
onClick={() => graph?.redo()}
|
onClick={handleRedo}
|
||||||
/>
|
disabled={!(graph as any)?.history?.canRedo()}
|
||||||
|
>
|
||||||
|
重做
|
||||||
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Space.Compact>
|
</Space.Compact>
|
||||||
<Space.Compact>
|
<Space.Compact>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user