From 60dac8d33dd590b9da47f470181bb6ca12bb468c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=94=E6=A2=A6?= Date: Tue, 14 Oct 2025 11:52:37 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=E8=8A=82=E7=82=B9=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E9=94=AE=E5=88=A0=E9=99=A4=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/x-flow/src/XFlow.tsx | 49 +++++++++++++++------------- packages/x-flow/src/hooks/useFlow.ts | 16 +++++---- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/packages/x-flow/src/XFlow.tsx b/packages/x-flow/src/XFlow.tsx index b35f14e75..23a0abf87 100644 --- a/packages/x-flow/src/XFlow.tsx +++ b/packages/x-flow/src/XFlow.tsx @@ -99,7 +99,7 @@ const XFlow: FC = memo(props => { connectionLineComponent, } = props; const nodeEditorRef = useRef(null); - const { copyNode, pasteNodeSimple } = useFlow(); + const { copyNode, pasteNodeSimple, deleteNode } = useFlow(); const { undo, redo } = useTemporalStore(); const isNodeCopyingRef = useRef(false); // 是否正在进行节点复制 const lastClickedNodeRef = useRef(false); // 最后一次点击是否是节点 @@ -437,30 +437,35 @@ const XFlow: FC = memo(props => { message.warning(`${blockedNodes.map(n => n.data?.title || n.id).join(', ')}节点不允许删除!`); return false; } - return true + if (nodesToDelete?.length) { + setOpenPanel(false); + } + return true; }} onConnect={onConnect} onNodesChange={changes => { - changes.forEach(change => { - if (change.type === 'remove') { - record(() => { - onNodesChange([change]); - }); - } else { - onNodesChange([change]); - } - }); + const removeChanges = changes.filter(c => c.type === 'remove'); + const otherChanges = changes.filter(c => c.type !== 'remove'); + // 处理删除操作:使用与右键菜单相同的 deleteNode 方法 + if (removeChanges.length > 0) { + removeChanges.forEach(change => { + if ('id' in change) { + deleteNode(change.id); + // 如果删除的是当前激活的节点,关闭面板 + if (change.id === activeNode?.id) { + setActiveNode(null); + } + } + }); + } + + // 其他操作使用 ReactFlow 的默认处理 + if (otherChanges.length > 0) { + onNodesChange(otherChanges); + } }} onEdgesChange={changes => { - changes.forEach(change => { - if (change.type === 'remove') { - record(() => { - onEdgesChange([change]); - }); - } else { - onEdgesChange([change]); - } - }); + onEdgesChange(changes); }} onEdgeMouseEnter={(_, edge: any) => { if(!edge.style.stroke || edge.style.stroke === '#c9c9c9'){ @@ -472,8 +477,8 @@ const XFlow: FC = memo(props => { getUpdateEdgeConfig(edge, '#c9c9c9'); } }} - onNodesDelete={() => { - setActiveNode(null); + onNodesDelete={(nodes) => { + // setActiveNode(null); }} onNodeClick={(event, node) => { onNodeClick && onNodeClick(event, node); diff --git a/packages/x-flow/src/hooks/useFlow.ts b/packages/x-flow/src/hooks/useFlow.ts index 14455810c..04b2232b1 100644 --- a/packages/x-flow/src/hooks/useFlow.ts +++ b/packages/x-flow/src/hooks/useFlow.ts @@ -129,14 +129,20 @@ export const useFlow = () => { // 关闭mousemove监听 storeApi.getState().setIsAddingNode(false); + // 清除 selected 状态,避免粘贴的节点被意外选中 + const copyNodesWithoutSelection = storeApi.getState().copyNodes.map(node => ({ + ...node, + selected: false, + })); + const newEdges = { id: uuid(), source: nodeId, - target: storeApi.getState().copyNodes[0].id, + target: copyNodesWithoutSelection[0].id, ...data }; record(() => { - storeApi.getState().addNodes(storeApi.getState().copyNodes, false); + storeApi.getState().addNodes(copyNodesWithoutSelection, false); }) storeApi.getState().addEdges(newEdges); storeApi.setState({ @@ -163,8 +169,10 @@ export const useFlow = () => { x: mousePosition.elementX, y: mousePosition.elementY, }); + // 清除 selected 状态,避免粘贴的节点被意外选中 const copyNodes = storeApi.getState().copyNodes.map(node => ({ ...node, + selected: false, position: { x: flowPos.x, y: flowPos.y, @@ -191,10 +199,6 @@ export const useFlow = () => { .edges.filter( edge => edge.source !== nodeId && edge.target !== nodeId ), - }); - }); - record(() => { - storeApi.setState({ nodes: storeApi.getState().nodes.filter(node => node.id !== nodeId), }); }); From 03be20590f0f0987b4bd0d44d68936f020d32acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=94=E6=A2=A6?= Date: Tue, 14 Oct 2025 17:57:03 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix=EF=BC=9A=E5=88=A0=E9=99=A4=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/x-flow/src/XFlow.tsx | 55 ++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/packages/x-flow/src/XFlow.tsx b/packages/x-flow/src/XFlow.tsx index 23a0abf87..32e534410 100644 --- a/packages/x-flow/src/XFlow.tsx +++ b/packages/x-flow/src/XFlow.tsx @@ -103,6 +103,7 @@ const XFlow: FC = memo(props => { const { undo, redo } = useTemporalStore(); const isNodeCopyingRef = useRef(false); // 是否正在进行节点复制 const lastClickedNodeRef = useRef(false); // 最后一次点击是否是节点 + const isPanelRenderingRef = useRef(false); // 面板是否正在渲染中 useEffect(() => { zoomTo(0.8); @@ -333,11 +334,14 @@ const XFlow: FC = memo(props => { onClick={async e => { // 记录用户点击了节点 lastClickedNodeRef.current = true; + // 标记面板开始渲染 + isPanelRenderingRef.current = true; if (nodeEditorRef?.current?.validateForm) { const result = await nodeEditorRef?.current?.validateForm(); if (!result) { message.error('请检查必填项!'); + isPanelRenderingRef.current = false; return; } } @@ -353,6 +357,10 @@ const XFlow: FC = memo(props => { setOpenPanel(true); } setOpenLogPanel(true); + + setTimeout(() => { + isPanelRenderingRef.current = false; + }, 1000); // 给面板一点时间完成渲染 }} onDelete={()=>{ // 删除节点并关闭弹窗 @@ -437,31 +445,38 @@ const XFlow: FC = memo(props => { message.warning(`${blockedNodes.map(n => n.data?.title || n.id).join(', ')}节点不允许删除!`); return false; } - if (nodesToDelete?.length) { + + // 检查是否删除的是当前激活的节点 + const hasActiveNode = nodesToDelete.some(node => node.id === activeNode?.id); + if (hasActiveNode) { + // 立即关闭面板和清除激活状态 setOpenPanel(false); + setActiveNode(null); } - return true; - }} - onConnect={onConnect} - onNodesChange={changes => { - const removeChanges = changes.filter(c => c.type === 'remove'); - const otherChanges = changes.filter(c => c.type !== 'remove'); - // 处理删除操作:使用与右键菜单相同的 deleteNode 方法 - if (removeChanges.length > 0) { - removeChanges.forEach(change => { - if ('id' in change) { - deleteNode(change.id); - // 如果删除的是当前激活的节点,关闭面板 - if (change.id === activeNode?.id) { - setActiveNode(null); - } - } + + const executeDelete = () => { + nodesToDelete.forEach(node => { + deleteNode(node.id); }); + }; + + // 如果面板正在渲染中,延迟执行删除避免状态冲突 + if (isPanelRenderingRef.current) { + setTimeout(() => { + executeDelete(); + }, 150); + } else { + // 面板未在渲染,立即执行删除 + executeDelete(); } - // 其他操作使用 ReactFlow 的默认处理 - if (otherChanges.length > 0) { - onNodesChange(otherChanges); + return false; + }} + onConnect={onConnect} + onNodesChange={changes => { + const nonRemoveChanges = changes.filter(c => c.type !== 'remove'); + if (nonRemoveChanges.length > 0) { + onNodesChange(nonRemoveChanges); } }} onEdgesChange={changes => {