|
|
|
|
@ -78,6 +78,7 @@ interface FlowEditorMainProps {
|
|
|
|
|
editNode: (node: Node) => void;
|
|
|
|
|
editEdge: (edge: Edge) => void;
|
|
|
|
|
copyNode: (node: Node) => void;
|
|
|
|
|
pasteNode: (position: { x: number; y: number }) => void;
|
|
|
|
|
addNodeOnEdge: (nodeType: string, node: any) => void;
|
|
|
|
|
addNodeOnPane: (nodeType: string, position: { x: number, y: number }, node?: any) => void;
|
|
|
|
|
handleAddNode: (nodeType: string, node: any) => void;
|
|
|
|
|
@ -130,6 +131,7 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
|
|
|
|
|
editNode,
|
|
|
|
|
editEdge,
|
|
|
|
|
copyNode,
|
|
|
|
|
pasteNode,
|
|
|
|
|
addNodeOnEdge,
|
|
|
|
|
addNodeOnPane,
|
|
|
|
|
handleAddNode,
|
|
|
|
|
@ -224,28 +226,67 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
|
|
|
|
|
// 监听键盘事件实现快捷键
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
|
|
|
// Ctrl+Z 撤销
|
|
|
|
|
if (e.ctrlKey && e.key === 'z' && !e.shiftKey && canUndo) {
|
|
|
|
|
// 如果当前正在运行或不是默认模式,不处理复制粘贴快捷键
|
|
|
|
|
const canEdit = !currentAppIsRunning && useDefault;
|
|
|
|
|
// 兼容 Mac (metaKey) 和 Windows/Linux (ctrlKey)
|
|
|
|
|
const isModifierKey = e.ctrlKey || e.metaKey;
|
|
|
|
|
|
|
|
|
|
// Ctrl/Cmd+Z 撤销
|
|
|
|
|
if (isModifierKey && e.key === 'z' && !e.shiftKey && canUndo) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
undo();
|
|
|
|
|
}
|
|
|
|
|
// Ctrl+Shift+Z 重做
|
|
|
|
|
if (e.ctrlKey && e.shiftKey && e.key === 'Z' && canRedo) {
|
|
|
|
|
// Ctrl/Cmd+Shift+Z 重做
|
|
|
|
|
if (isModifierKey && e.shiftKey && e.key === 'Z' && canRedo) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
redo();
|
|
|
|
|
}
|
|
|
|
|
// Ctrl+Y 重做
|
|
|
|
|
if (e.ctrlKey && e.key === 'y' && canRedo) {
|
|
|
|
|
// Ctrl/Cmd+Y 重做
|
|
|
|
|
if (isModifierKey && e.key === 'y' && canRedo) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
redo();
|
|
|
|
|
}
|
|
|
|
|
// Ctrl/Cmd+C 复制选中的节点
|
|
|
|
|
if (isModifierKey && e.key === 'c' && canEdit) {
|
|
|
|
|
// 获取当前选中的节点
|
|
|
|
|
const selectedNode = nodes.find(node => node.selected);
|
|
|
|
|
if (selectedNode) {
|
|
|
|
|
// 不允许复制开始和结束节点
|
|
|
|
|
if (selectedNode.type === 'start' || selectedNode.type === 'end') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
copyNode(selectedNode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Ctrl/Cmd+V 粘贴节点
|
|
|
|
|
if (isModifierKey && e.key === 'v' && canEdit) {
|
|
|
|
|
const copiedNodeStr = localStorage.getItem('copiedNode');
|
|
|
|
|
if (copiedNodeStr) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
// 获取当前视口中心位置
|
|
|
|
|
if (reactFlowInstance) {
|
|
|
|
|
const viewport = reactFlowInstance.getViewport();
|
|
|
|
|
const { x, y, zoom } = viewport;
|
|
|
|
|
// 获取画布容器的尺寸
|
|
|
|
|
const wrapper = reactFlowWrapper.current;
|
|
|
|
|
if (wrapper) {
|
|
|
|
|
const rect = wrapper.getBoundingClientRect();
|
|
|
|
|
// 计算视口中心点在流程图坐标系中的位置
|
|
|
|
|
const centerX = (-x + rect.width / 2) / zoom;
|
|
|
|
|
const centerY = (-y + rect.height / 2) / zoom;
|
|
|
|
|
pasteNode({ x: centerX, y: centerY });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener('keydown', handleKeyDown);
|
|
|
|
|
};
|
|
|
|
|
}, [undo, redo, canUndo, canRedo]);
|
|
|
|
|
}, [undo, redo, canUndo, canRedo, currentAppIsRunning, useDefault, nodes, copyNode, pasteNode, reactFlowInstance]);
|
|
|
|
|
|
|
|
|
|
// 处理流程发布
|
|
|
|
|
const handlePublish = () => {
|
|
|
|
|
|