From 4234aa75d9840df6e1067386995c84d0c303872c Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 1 Sep 2025 10:32:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(flowEditor):=20=E6=B7=BB=E5=8A=A0=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E7=BC=96=E8=BE=91=E5=8A=9F=E8=83=BD=E5=92=8C=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E4=BF=9D=E5=AD=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增节点双击事件,打开节点编辑弹窗 - 实现节点编辑逻辑,支持不同类型的节点 - 添加保存节点和边数据到服务器的功能 - 优化节点和边变化时的数据处理 --- .../flowEditor/components/nodeEditModal.tsx | 111 ++++++++++++++++ src/pages/flowEditor/index.tsx | 118 ++++++++++++++++-- 2 files changed, 218 insertions(+), 11 deletions(-) create mode 100644 src/pages/flowEditor/components/nodeEditModal.tsx diff --git a/src/pages/flowEditor/components/nodeEditModal.tsx b/src/pages/flowEditor/components/nodeEditModal.tsx new file mode 100644 index 0000000..71b0e89 --- /dev/null +++ b/src/pages/flowEditor/components/nodeEditModal.tsx @@ -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 = ({ + 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 ( +
+

开始节点设置

+
+ + setTitle(e.target.value)} + style={{ width: '100%', padding: '8px', margin: '10px 0' }} + /> +
+
+ ); + case 'end': + return ( +
+

结束节点设置

+
+ + setTitle(e.target.value)} + style={{ width: '100%', padding: '8px', margin: '10px 0' }} + /> +
+
+ ); + case 'basic': + default: + return ( +
+

基础节点设置

+
+ + setTitle(e.target.value)} + style={{ width: '100%', padding: '8px', margin: '10px 0' }} + /> +
+
+ ); + } + }; + + return ( + handleSave()} + onCancel={() => handleClose()} + autoFocus={false} + focusLock={true} + > + {renderContent()} + + ); +}; + +export default NodeEditModal; \ No newline at end of file diff --git a/src/pages/flowEditor/index.tsx b/src/pages/flowEditor/index.tsx index 97d9ce8..677233e 100644 --- a/src/pages/flowEditor/index.tsx +++ b/src/pages/flowEditor/index.tsx @@ -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(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 (
{ onDragOver={onDragOver} onNodeContextMenu={onNodeContextMenu} onEdgeContextMenu={onEdgeContextMenu} + onNodeDoubleClick={onNodeDoubleClick} onPaneClick={onPaneClick} onPaneContextMenu={onPaneClick} fitView @@ -224,11 +309,13 @@ const FlowEditor: React.FC = () => { > - {/**/} - {/*
从左侧拖拽节点到画布中
*/} - {/*
*/} + +
从左侧拖拽节点到画布中
+ +
+ {/*节点右键上下文*/} {menu && menu.type === 'node' && (
{
)} + {/*边右键上下文*/} {menu && menu.type === 'edge' && (
{ />
)} + + {/*节点双击/节点编辑上下文*/} +
);