1、根据上一篇文章React Flow快速上手教程-CSDN博客构建好reate xyflow项目后,将其中的Flow.js文件内容替换为以下脚本,包含添加/删除节点和边、修改节点名称的功能
import React, { useState, useCallback, useRef, useEffect } from \'react\';import { ReactFlow, MiniMap, Controls, Background, useNodesState, useEdgesState, addEdge, MarkerType} from \'@xyflow/react\';import \'@xyflow/react/dist/style.css\';function Flow() { // 初始节点数据 const initialNodes = [ { id: \'1\', position: { x: 250, y: 5 }, data: { label: \'Node 1\' } }, { id: \'2\', position: { x: 100, y: 100 }, data: { label: \'Node 2\' } }, ]; // 使用 XYFlow 的状态管理钩子 const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); // 选中的节点状态(用于创建边) const [selectedNodes, setSelectedNodes] = useState({ source: null, target: null }); // 当前选中的元素(用于删除/重命名) const [selectedElement, setSelectedElement] = useState(null); // 节点重命名相关状态 const [isRenaming, setIsRenaming] = useState(false); const [newNodeName, setNewNodeName] = useState(\'\'); const renameInputRef = useRef(null); /** * 处理节点点击事件 * @param {Event} event - 点击事件对象 * @param {Object} node - 被点击的节点数据 */ const handleNodeClick = (event, node) => { // 如果正在重命名,则不做其他处理 if (isRenaming) return; setSelectedElement(node); // 选择源节点或目标节点 if (!selectedNodes.source) { setSelectedNodes({ source: node.id, target: null }); } else if (!selectedNodes.target && node.id !== selectedNodes.source) { setSelectedNodes(prev => ({ ...prev, target: node.id })); } else { setSelectedNodes({ source: node.id, target: null }); } }; /** * 处理边点击事件 * @param {Event} event - 点击事件对象 * @param {Object} edge - 被点击的边数据 */ const handleEdgeClick = (event, edge) => { if (isRenaming) return; setSelectedElement(edge); }; /** * 处理画布点击事件(点击空白处取消选择) */ const handlePaneClick = () => { if (isRenaming) return; setSelectedElement(null); }; /** * 添加新节点 */ const handleAddNode = () => { const newNodeId = `${nodes.length + 1}`; const newNode = { id: newNodeId, position: { x: Math.random() * 400, y: Math.random() * 400 }, data: { label: `Node ${newNodeId}` }, }; setNodes((nds) => [...nds, newNode]); }; /** * 添加新边(连接两个节点) */ const handleAddEdge = () => { if (selectedNodes.source && selectedNodes.target) { const newEdge = { id: `edge-${selectedNodes.source}-${selectedNodes.target}-${edges.length}`, source: selectedNodes.source, target: selectedNodes.target, // 边样式配置 markerEnd: { type: MarkerType.ArrowClosed, }, }; setEdges((eds) => addEdge(newEdge, eds)); setSelectedNodes({ source: null, target: null }); } }; /** * 删除选中的元素(节点或边) */ const handleDeleteElement = () => { if (!selectedElement || isRenaming) return; // 删除节点(会自动删除相关联的边) if (selectedElement.type === \'node\' || selectedElement.hasOwnProperty(\'data\')) { setNodes((nds) => nds.filter((node) => node.id !== selectedElement.id)); } // 删除边 else if (selectedElement.type === \'edge\' || selectedElement.hasOwnProperty(\'source\')) { setEdges((eds) => eds.filter((edge) => edge.id !== selectedElement.id)); } setSelectedElement(null); }; /** * 开始重命名节点 */ const startRenaming = () => { if (!selectedElement || !selectedElement.data) return; setIsRenaming(true); setNewNodeName(selectedElement.data.label); }; /** * 完成重命名 */ const finishRenaming = () => { if (!selectedElement || !selectedElement.data) return; setNodes(nodes.map(node => { if (node.id === selectedElement.id) { return { ...node, data: { ...node.data, label: newNodeName } }; } return node; })); setIsRenaming(false); setNewNodeName(\'\'); }; /** * 取消重命名 */ const cancelRenaming = () => { setIsRenaming(false); setNewNodeName(\'\'); }; /** * 处理重命名输入框的键盘事件 * @param {Event} e - 键盘事件对象 */ const handleRenameKeyDown = (e) => { if (e.key === \'Enter\') { finishRenaming(); } else if (e.key === \'Escape\') { cancelRenaming(); } }; // 自动聚焦到重命名输入框 useEffect(() => { if (isRenaming && renameInputRef.current) { renameInputRef.current.focus(); renameInputRef.current.select(); } }, [isRenaming]); /** * XYFlow 的连接回调(拖拽创建边时触发) */ const onConnect = useCallback( (params) => { const newEdge = { ...params, id: `edge-${params.source}-${params.target}-${edges.length}`, markerEnd: { type: MarkerType.ArrowClosed, }, }; return setEdges((eds) => addEdge(newEdge, eds)); }, [setEdges, edges.length] ); return ( <div style={{ width: \'100%\', height: \'100vh\' }}> { if (isRenaming) return; if ([\'Delete\', \'Backspace\'].includes(event.key)) { handleDeleteElement(); } }} > {/* 自定义节点渲染 - 添加重命名输入框 */} {isRenaming && selectedElement && ( <div style={{ position: \'absolute\', top: selectedElement.position.y, left: selectedElement.position.x, zIndex: 10, backgroundColor: \'white\', padding: \'5px\', borderRadius: \'3px\', boxShadow: \'0 0 5px rgba(0,0,0,0.2)\' }} > setNewNodeName(e.target.value)} onKeyDown={handleRenameKeyDown} onBlur={finishRenaming} style={{ width: \'150px\', padding: \'5px\', border: \'1px solid #ddd\', borderRadius: \'3px\' }} /> <div style={{ fontSize: \'12px\', color: \'#666\', marginTop: \'5px\' }}> Press Enter to save, Esc to cancel