From afa5de10301dd54ecec8ef62db8ac206b7b65681 Mon Sep 17 00:00:00 2001 From: ZLY Date: Tue, 26 Aug 2025 09:41:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(flowEditor):=20=E5=A2=9E=E5=8A=A0=E5=88=9D?= =?UTF-8?q?=E7=89=88=E6=B5=81=E7=A8=8B=E7=BC=96=E8=BE=91=E5=99=A8=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增加开始结束节点,增加编辑器侧边栏 --- src/pages/flowEditor/index.tsx | 110 +++++++++++++----- .../node/draggableNode/DraggableNode.tsx | 30 +++++ src/pages/flowEditor/node/endNode/EndNode.tsx | 24 ++++ .../flowEditor/node/startNode/StartNode.tsx | 24 ++++ src/pages/flowEditor/sideBar/sideBar.tsx | 41 +++++++ 5 files changed, 203 insertions(+), 26 deletions(-) create mode 100644 src/pages/flowEditor/node/draggableNode/DraggableNode.tsx create mode 100644 src/pages/flowEditor/node/endNode/EndNode.tsx create mode 100644 src/pages/flowEditor/node/startNode/StartNode.tsx create mode 100644 src/pages/flowEditor/sideBar/sideBar.tsx diff --git a/src/pages/flowEditor/index.tsx b/src/pages/flowEditor/index.tsx index a1dfe02..be985b3 100644 --- a/src/pages/flowEditor/index.tsx +++ b/src/pages/flowEditor/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useRef } from 'react'; import { ReactFlow, applyNodeChanges, @@ -7,58 +7,78 @@ import { Background, Controls, Node, - Edge + Edge, + ReactFlowProvider, + useReactFlow, + NodeTypes, + Panel } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import TextUpdaterNode from './node/textUpdateNode/TextUpdaterNode'; +import StartNode from './node/startNode/StartNode'; +import EndNode from './node/endNode/EndNode'; +import DraggableNode from './node/draggableNode/DraggableNode'; +import SideBar from './sideBar/sideBar'; -const nodeTypes = { - textUpdater: TextUpdaterNode +const nodeTypes: NodeTypes = { + textUpdater: TextUpdaterNode, + start: StartNode, + end: EndNode, + draggable: DraggableNode }; const initialNodes: Node[] = [ { - id: 'n1', + id: 'start-node', position: { x: 0, y: 0 }, - data: { label: 'Node 1' }, - type: 'input' + data: { label: '开始' }, + type: 'start' + }, + { + id: 'end-node', + position: { x: 300, y: 200 }, + data: { label: '结束' }, + type: 'end' }, { id: 'node-1', type: 'textUpdater', position: { x: 150, y: 0 }, data: { value: 123 } - }, - { - id: 'n2', - position: { x: 100, y: 100 }, - data: { label: 'Custom Node' } } ]; const initialEdges: Edge[] = [ { - id: 'n1-n2', - source: 'n1', - target: 'n2' - }, - { - id: 'n1-node-1', - source: 'n1', + id: 'start-node-1', + source: 'start-node', target: 'node-1', - targetHandle: 'a' + sourceHandle: 'start-source' }, { - id: 'n2-node-1', - source: 'n2', - target: 'node-1', - targetHandle: 'a1' + id: 'node-1-end', + source: 'node-1', + target: 'end-node', + targetHandle: 'end-target' } ]; +const FlowEditorWithProvider: React.FC = () => { + return ( +
+ + + + +
+ ); +}; + const FlowEditor: React.FC = () => { const [nodes, setNodes] = useState(initialNodes); const [edges, setEdges] = useState(initialEdges); + const reactFlowInstance = useReactFlow(); + const reactFlowWrapper = useRef(null); const onNodesChange = useCallback( (changes: any) => setNodes((nodesSnapshot) => applyNodeChanges(changes, nodesSnapshot)), @@ -73,8 +93,41 @@ const FlowEditor: React.FC = () => { [] ); + const onDragOver = useCallback((event: React.DragEvent) => { + event.preventDefault(); + event.dataTransfer.dropEffect = 'move'; + }, []); + + const onDrop = useCallback( + (event: React.DragEvent) => { + event.preventDefault(); + + if (!reactFlowInstance) return; + + const type = event.dataTransfer.getData('application/reactflow'); + if (typeof type === 'undefined' || !type) { + return; + } + + const position = reactFlowInstance.screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }); + + const newNode = { + id: `${type}-${Date.now()}`, + type, + position, + data: { label: `${type} node` }, + }; + + setNodes((nds) => nds.concat(newNode)); + }, + [reactFlowInstance] + ); + return ( -
+
{ onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} + onDrop={onDrop} + onDragOver={onDragOver} fitView > + {/**/} + {/*
从左侧拖拽节点到画布中
*/} + {/*
*/}
); }; -export default React.memo(FlowEditor); \ No newline at end of file +export default FlowEditorWithProvider; \ No newline at end of file diff --git a/src/pages/flowEditor/node/draggableNode/DraggableNode.tsx b/src/pages/flowEditor/node/draggableNode/DraggableNode.tsx new file mode 100644 index 0000000..d1a2a8e --- /dev/null +++ b/src/pages/flowEditor/node/draggableNode/DraggableNode.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { Handle, Position } from '@xyflow/react'; + +const DraggableNode = ({ data }: { data: any }) => { + return ( +
+
{data.label || '任务节点'}
+ + +
+ ); +}; + +export default DraggableNode; \ No newline at end of file diff --git a/src/pages/flowEditor/node/endNode/EndNode.tsx b/src/pages/flowEditor/node/endNode/EndNode.tsx new file mode 100644 index 0000000..4f21b88 --- /dev/null +++ b/src/pages/flowEditor/node/endNode/EndNode.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Handle, Position } from '@xyflow/react'; + +const EndNode = ({ data }: { data: any }) => { + return ( +
+
结束
+ +
+ ); +}; + +export default EndNode; \ No newline at end of file diff --git a/src/pages/flowEditor/node/startNode/StartNode.tsx b/src/pages/flowEditor/node/startNode/StartNode.tsx new file mode 100644 index 0000000..8e55995 --- /dev/null +++ b/src/pages/flowEditor/node/startNode/StartNode.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Handle, Position } from '@xyflow/react'; + +const StartNode = ({ data }: { data: any }) => { + return ( +
+
开始
+ +
+ ); +}; + +export default StartNode; \ No newline at end of file diff --git a/src/pages/flowEditor/sideBar/sideBar.tsx b/src/pages/flowEditor/sideBar/sideBar.tsx new file mode 100644 index 0000000..0669a75 --- /dev/null +++ b/src/pages/flowEditor/sideBar/sideBar.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Card } from '@arco-design/web-react'; + +const onDragStart = (event: React.DragEvent, nodeType: string) => { + event.dataTransfer.setData('application/reactflow', nodeType); + event.dataTransfer.effectAllowed = 'move'; +}; + +const SideBar: React.FC = () => { + return ( +
+
+

节点库

+

将节点拖拽到画布中

+
+ + onDragStart(event, 'draggable')} + > +
任务节点 1
+
+ + onDragStart(event, 'textUpdater')} + > +
文本节点
+
+
+ ); +}; + +export default SideBar; \ No newline at end of file