feat(flowEditor): 添加节点编辑功能和数据保存功能

- 新增节点双击事件,打开节点编辑弹窗
- 实现节点编辑逻辑,支持不同类型的节点
- 添加保存节点和边数据到服务器的功能
- 优化节点和边变化时的数据处理
master
钟良源 5 months ago
parent 1a7b31a35e
commit 4234aa75d9

@ -0,0 +1,111 @@
import React, { useState, useEffect } from 'react';
import { Node } from '@xyflow/react';
import { Modal, Button } from '@arco-design/web-react';
interface NodeEditModalProps {
node: Node | any;
isOpen: boolean;
onSave: (data: any) => void;
onClose: () => void;
}
const NodeEditModal: React.FC<NodeEditModalProps> = ({
node,
isOpen,
onSave,
onClose
}) => {
const [title, setTitle] = useState('');
const [visible, setVisible] = React.useState(false);
useEffect(() => {
if (node) {
setTitle(node.data?.title || '');
}
}, [node]);
useEffect(() => {
setVisible(isOpen);
}, [isOpen]);
if (!isOpen || !node) return null;
const handleSave = () => {
setVisible(false);
onSave({ title });
};
const handleClose = () => {
setVisible(false);
onClose();
};
// 根据节点类型渲染不同的内容
const renderContent = () => {
const nodeType = node?.type || 'basic';
switch (nodeType) {
case 'start':
return (
<div>
<h4></h4>
<div>
<label>:</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
style={{ width: '100%', padding: '8px', margin: '10px 0' }}
/>
</div>
</div>
);
case 'end':
return (
<div>
<h4></h4>
<div>
<label>:</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
style={{ width: '100%', padding: '8px', margin: '10px 0' }}
/>
</div>
</div>
);
case 'basic':
default:
return (
<div>
<h4></h4>
<div>
<label>:</label>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
style={{ width: '100%', padding: '8px', margin: '10px 0' }}
/>
</div>
</div>
);
}
};
return (
<Modal
title={'节点标题:' + title}
visible={visible}
onOk={() => handleSave()}
onCancel={() => handleClose()}
autoFocus={false}
focusLock={true}
>
{renderContent()}
</Modal>
);
};
export default NodeEditModal;

@ -13,7 +13,8 @@ import {
useReactFlow,
EdgeTypes,
SelectionMode,
useStoreApi
useStoreApi,
Panel
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { nodeTypeMap, nodeTypes, registerNodeType } from './node';
@ -24,6 +25,7 @@ import LocalNode from '@/pages/flowEditor/node/localNode/LocalNode';
import CustomEdge from './components/customEdge';
import NodeContextMenu from './components/nodeContextMenu';
import EdgeContextMenu from './components/edgeContextMenu';
import NodeEditModal from './components/nodeEditModal';
const edgeTypes: EdgeTypes = {
custom: CustomEdge
@ -53,13 +55,26 @@ const FlowEditor: React.FC = () => {
} | null>(null);
const store = useStoreApi();
// 添加编辑弹窗相关状态
const [editingNode, setEditingNode] = useState<Node | null>(null);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const onNodesChange = useCallback(
(changes: any) => setNodes((nodesSnapshot) => applyNodeChanges(changes, nodesSnapshot)),
[]
(changes: any) => {
const newNodes = applyNodeChanges(changes, nodes);
setNodes(newNodes);
// 如果需要在节点变化时执行某些操作,可以在这里添加
},
[nodes]
);
const onEdgesChange = useCallback(
(changes: any) => setEdges((edgesSnapshot) => applyEdgeChanges(changes, edgesSnapshot)),
[]
(changes: any) => {
const newEdges = applyEdgeChanges(changes, edges);
setEdges(newEdges);
// 如果需要在边变化时执行某些操作,可以在这里添加
},
[edges]
);
const onConnect = useCallback(
@ -69,7 +84,7 @@ const FlowEditor: React.FC = () => {
// 边重新连接处理
const onReconnect = useCallback(
(oldEdge: Edge, newConnection: any) =>
(oldEdge: Edge, newConnection: any) =>
setEdges((els) => reconnectEdge(oldEdge, newConnection, els)),
[]
);
@ -142,6 +157,16 @@ const FlowEditor: React.FC = () => {
[setMenu]
);
// 节点双击处理
const onNodeDoubleClick = useCallback(
(event: React.MouseEvent, node: Node) => {
console.log('双击');
setEditingNode(node);
setIsEditModalOpen(true);
},
[]
);
// 边右键菜单处理
const onEdgeContextMenu = useCallback(
(event: React.MouseEvent, edge: Edge) => {
@ -163,6 +188,31 @@ const FlowEditor: React.FC = () => {
// 点击画布其他区域关闭菜单
const onPaneClick = useCallback(() => setMenu(null), [setMenu]);
// 关闭编辑弹窗
const closeEditModal = useCallback(() => {
setIsEditModalOpen(false);
setEditingNode(null);
}, []);
// 保存节点编辑
const saveNodeEdit = useCallback((updatedData: any) => {
const updatedNodes = nodes.map((node) => {
if (node.id === editingNode?.id) {
return {
...node,
data: { ...node.data, ...updatedData }
};
}
return node;
});
setNodes(updatedNodes);
closeEditModal();
// TODO 如果需要在节点编辑后立即保存到服务器,可以调用保存函数
// saveFlowDataToServer();
}, [nodes, editingNode, closeEditModal]);
// 删除节点
const deleteNode = useCallback((node: Node) => {
setNodes((nds) => nds.filter((n) => n.id !== node.id));
@ -178,9 +228,9 @@ const FlowEditor: React.FC = () => {
// 编辑节点
const editNode = useCallback((node: Node) => {
// 这里可以实现节点编辑逻辑
console.log('编辑节点:', node);
setMenu(null);
setEditingNode(node);
setIsEditModalOpen(true);
}, []);
// 编辑边
@ -197,6 +247,40 @@ const FlowEditor: React.FC = () => {
setMenu(null);
}, []);
// 保存所有节点和边数据到服务器
const saveFlowDataToServer = useCallback(async () => {
try {
// 准备要发送到服务器的数据
const flowData = {
nodes: nodes,
edges: edges
};
// TODO 接口对接后修改后续的更新操作
console.log('flowData:', flowData);
return;
// 发送到服务器的示例代码需要根据您的实际API进行调整
const response = await fetch('/api/flow/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(flowData)
});
if (response.ok) {
console.log('Flow data saved successfully');
// 可以添加成功提示
}
else {
console.error('Failed to save flow data');
// 可以添加失败提示
}
} catch (error) {
console.error('Error saving flow data:', error);
// 可以添加错误提示
}
}, [nodes, edges]);
return (
<div ref={reactFlowWrapper} style={{ width: '100%', height: '100%', position: 'relative' }}>
<ReactFlow
@ -212,6 +296,7 @@ const FlowEditor: React.FC = () => {
onDragOver={onDragOver}
onNodeContextMenu={onNodeContextMenu}
onEdgeContextMenu={onEdgeContextMenu}
onNodeDoubleClick={onNodeDoubleClick}
onPaneClick={onPaneClick}
onPaneContextMenu={onPaneClick}
fitView
@ -224,11 +309,13 @@ const FlowEditor: React.FC = () => {
>
<Background />
<Controls />
{/*<Panel position="top-right">*/}
{/* <div>从左侧拖拽节点到画布中</div>*/}
{/*</Panel>*/}
<Panel position="top-right">
<div></div>
<button onClick={saveFlowDataToServer}></button>
</Panel>
</ReactFlow>
{/*节点右键上下文*/}
{menu && menu.type === 'node' && (
<div
style={{
@ -247,6 +334,7 @@ const FlowEditor: React.FC = () => {
</div>
)}
{/*边右键上下文*/}
{menu && menu.type === 'edge' && (
<div
style={{
@ -263,6 +351,14 @@ const FlowEditor: React.FC = () => {
/>
</div>
)}
{/*节点双击/节点编辑上下文*/}
<NodeEditModal
node={editingNode}
isOpen={isEditModalOpen}
onSave={saveNodeEdit}
onClose={closeEditModal}
/>
</div>
);

Loading…
Cancel
Save