|
|
|
|
@ -86,6 +86,7 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
|
|
|
|
|
editNode,
|
|
|
|
|
editEdge,
|
|
|
|
|
copyNode,
|
|
|
|
|
pasteNode, // 添加粘贴节点功能
|
|
|
|
|
|
|
|
|
|
// Node operations
|
|
|
|
|
addNodeOnEdge,
|
|
|
|
|
@ -200,11 +201,46 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
|
|
|
|
|
// 点击画布其他区域关闭菜单
|
|
|
|
|
const onPaneClick = useCallback(() => {
|
|
|
|
|
setMenu(null);
|
|
|
|
|
// 关闭添加节点菜单
|
|
|
|
|
setEdgeForNodeAdd(null);
|
|
|
|
|
setPositionForNodeAdd(null);
|
|
|
|
|
}, [setMenu]);
|
|
|
|
|
|
|
|
|
|
// 添加处理粘贴事件的函数
|
|
|
|
|
const handlePasteNode = useCallback((event: ClipboardEvent) => {
|
|
|
|
|
// 检查是否是粘贴操作且在画布区域内
|
|
|
|
|
if (event.clipboardData?.getData('text') === 'paste-node' && menu?.type === 'pane' && menu.position) {
|
|
|
|
|
pasteNode(menu.position);
|
|
|
|
|
setMenu(null);
|
|
|
|
|
}
|
|
|
|
|
}, [menu, pasteNode]);
|
|
|
|
|
|
|
|
|
|
// 添加键盘事件监听
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
|
|
|
// 检查是否按下了 Ctrl+V (Windows/Linux) 或 Cmd+V (Mac)
|
|
|
|
|
if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
|
|
|
|
|
// 触发自定义粘贴事件
|
|
|
|
|
const pasteEvent = new ClipboardEvent('paste', {
|
|
|
|
|
clipboardData: new DataTransfer()
|
|
|
|
|
});
|
|
|
|
|
pasteEvent.clipboardData?.setData('text', 'paste-node');
|
|
|
|
|
document.dispatchEvent(pasteEvent);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 为画布添加键盘事件监听
|
|
|
|
|
const canvasElement = reactFlowWrapper.current;
|
|
|
|
|
if (canvasElement) {
|
|
|
|
|
canvasElement.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
document.addEventListener('paste', handlePasteNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
if (canvasElement) {
|
|
|
|
|
canvasElement.removeEventListener('keydown', handleKeyDown);
|
|
|
|
|
}
|
|
|
|
|
document.removeEventListener('paste', handlePasteNode);
|
|
|
|
|
};
|
|
|
|
|
}, [handlePasteNode]);
|
|
|
|
|
|
|
|
|
|
// 监听边的变化,处理添加节点的触发
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const edgeToAddNode = edges.find(edge => edge.data?.addNodeTrigger);
|
|
|
|
|
@ -243,73 +279,80 @@ const FlowEditor: React.FC<{ initialData?: any, useDefault?: boolean }> = ({ ini
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<HistoryProvider
|
|
|
|
|
initialNodes={nodes}
|
|
|
|
|
initialEdges={edges}
|
|
|
|
|
onHistoryChange={(newNodes: Node[], newEdges: Edge[]) => {
|
|
|
|
|
setNodes(newNodes);
|
|
|
|
|
setEdges(newEdges);
|
|
|
|
|
}}
|
|
|
|
|
<div
|
|
|
|
|
ref={reactFlowWrapper}
|
|
|
|
|
style={{ width: '100%', height: '100%' }}
|
|
|
|
|
onContextMenu={(e) => e.preventDefault()}
|
|
|
|
|
tabIndex={0} // 使div可获得焦点以接收键盘事件
|
|
|
|
|
>
|
|
|
|
|
<FlowEditorMain
|
|
|
|
|
nodes={nodes}
|
|
|
|
|
edges={edges}
|
|
|
|
|
nodeTypes={nodeTypes}
|
|
|
|
|
setNodes={setNodes}
|
|
|
|
|
setEdges={setEdges}
|
|
|
|
|
useDefault={useDefault}
|
|
|
|
|
reactFlowInstance={reactFlowInstance}
|
|
|
|
|
reactFlowWrapper={reactFlowWrapper}
|
|
|
|
|
menu={menu}
|
|
|
|
|
setMenu={setMenu}
|
|
|
|
|
editingNode={editingNode}
|
|
|
|
|
setEditingNode={setEditingNode}
|
|
|
|
|
isEditModalOpen={isEditModalOpen}
|
|
|
|
|
setIsEditModalOpen={setIsEditModalOpen}
|
|
|
|
|
isDelete={isDelete}
|
|
|
|
|
setIsDelete={setIsDelete}
|
|
|
|
|
edgeForNodeAdd={edgeForNodeAdd}
|
|
|
|
|
setEdgeForNodeAdd={setEdgeForNodeAdd}
|
|
|
|
|
positionForNodeAdd={positionForNodeAdd}
|
|
|
|
|
setPositionForNodeAdd={setPositionForNodeAdd}
|
|
|
|
|
isRunning={isRunning}
|
|
|
|
|
initialData={initialData}
|
|
|
|
|
canvasDataMap={canvasDataMap}
|
|
|
|
|
|
|
|
|
|
// Event handlers
|
|
|
|
|
onNodesChange={onNodesChange}
|
|
|
|
|
onEdgesChange={onEdgesChange}
|
|
|
|
|
onConnect={onConnect}
|
|
|
|
|
onReconnect={onReconnect}
|
|
|
|
|
onDragOver={onDragOver}
|
|
|
|
|
onDrop={onDrop}
|
|
|
|
|
onNodeDrag={onNodeDrag}
|
|
|
|
|
onNodeDragStop={onNodeDragStop}
|
|
|
|
|
onNodeContextMenu={onNodeContextMenu}
|
|
|
|
|
onNodeDoubleClick={onNodeDoubleClick}
|
|
|
|
|
onEdgeContextMenu={onEdgeContextMenu}
|
|
|
|
|
onPaneContextMenu={onPaneContextMenu}
|
|
|
|
|
onPaneClick={onPaneClick}
|
|
|
|
|
|
|
|
|
|
// Menu handlers
|
|
|
|
|
closeEditModal={closeEditModal}
|
|
|
|
|
saveNodeEdit={saveNodeEdit}
|
|
|
|
|
deleteNode={deleteNode}
|
|
|
|
|
deleteEdge={deleteEdge}
|
|
|
|
|
editNode={editNode}
|
|
|
|
|
editEdge={editEdge}
|
|
|
|
|
copyNode={copyNode}
|
|
|
|
|
|
|
|
|
|
// Node operations
|
|
|
|
|
addNodeOnEdge={addNodeOnEdge}
|
|
|
|
|
addNodeOnPane={addNodeOnPane}
|
|
|
|
|
handleAddNode={handleAddNode}
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
saveFlowDataToServer={saveFlowDataToServer}
|
|
|
|
|
handleRun={handleRun}
|
|
|
|
|
/>
|
|
|
|
|
</HistoryProvider>
|
|
|
|
|
<HistoryProvider
|
|
|
|
|
initialNodes={nodes}
|
|
|
|
|
initialEdges={edges}
|
|
|
|
|
onHistoryChange={(newNodes: Node[], newEdges: Edge[]) => {
|
|
|
|
|
setNodes(newNodes);
|
|
|
|
|
setEdges(newEdges);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<FlowEditorMain
|
|
|
|
|
nodes={nodes}
|
|
|
|
|
edges={edges}
|
|
|
|
|
nodeTypes={nodeTypes}
|
|
|
|
|
setNodes={setNodes}
|
|
|
|
|
setEdges={setEdges}
|
|
|
|
|
useDefault={useDefault}
|
|
|
|
|
reactFlowInstance={reactFlowInstance}
|
|
|
|
|
reactFlowWrapper={reactFlowWrapper}
|
|
|
|
|
menu={menu}
|
|
|
|
|
setMenu={setMenu}
|
|
|
|
|
editingNode={editingNode}
|
|
|
|
|
setEditingNode={setEditingNode}
|
|
|
|
|
isEditModalOpen={isEditModalOpen}
|
|
|
|
|
setIsEditModalOpen={setIsEditModalOpen}
|
|
|
|
|
isDelete={isDelete}
|
|
|
|
|
setIsDelete={setIsDelete}
|
|
|
|
|
edgeForNodeAdd={edgeForNodeAdd}
|
|
|
|
|
setEdgeForNodeAdd={setEdgeForNodeAdd}
|
|
|
|
|
positionForNodeAdd={positionForNodeAdd}
|
|
|
|
|
setPositionForNodeAdd={setPositionForNodeAdd}
|
|
|
|
|
isRunning={isRunning}
|
|
|
|
|
initialData={initialData}
|
|
|
|
|
canvasDataMap={canvasDataMap}
|
|
|
|
|
|
|
|
|
|
// Event handlers
|
|
|
|
|
onNodesChange={onNodesChange}
|
|
|
|
|
onEdgesChange={onEdgesChange}
|
|
|
|
|
onConnect={onConnect}
|
|
|
|
|
onReconnect={onReconnect}
|
|
|
|
|
onDragOver={onDragOver}
|
|
|
|
|
onDrop={onDrop}
|
|
|
|
|
onNodeDrag={onNodeDrag}
|
|
|
|
|
onNodeDragStop={onNodeDragStop}
|
|
|
|
|
onNodeContextMenu={onNodeContextMenu}
|
|
|
|
|
onNodeDoubleClick={onNodeDoubleClick}
|
|
|
|
|
onEdgeContextMenu={onEdgeContextMenu}
|
|
|
|
|
onPaneContextMenu={onPaneContextMenu}
|
|
|
|
|
onPaneClick={onPaneClick}
|
|
|
|
|
|
|
|
|
|
// Menu handlers
|
|
|
|
|
closeEditModal={closeEditModal}
|
|
|
|
|
saveNodeEdit={saveNodeEdit}
|
|
|
|
|
deleteNode={deleteNode}
|
|
|
|
|
deleteEdge={deleteEdge}
|
|
|
|
|
editNode={editNode}
|
|
|
|
|
editEdge={editEdge}
|
|
|
|
|
copyNode={copyNode}
|
|
|
|
|
|
|
|
|
|
// Node operations
|
|
|
|
|
addNodeOnEdge={addNodeOnEdge}
|
|
|
|
|
addNodeOnPane={addNodeOnPane}
|
|
|
|
|
handleAddNode={handleAddNode}
|
|
|
|
|
|
|
|
|
|
// Actions
|
|
|
|
|
saveFlowDataToServer={saveFlowDataToServer}
|
|
|
|
|
handleRun={handleRun}
|
|
|
|
|
/>
|
|
|
|
|
</HistoryProvider>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|