From 8b2a63e545be3551c3ecdf4431ee584835f5efdd Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Thu, 14 Mar 2024 11:19:17 +0800 Subject: [PATCH] fix --- .../[appId]/workflow/page.tsx | 43 ++++++++++++++++++- .../workflow/hooks/use-edges-interactions.ts | 41 ++++++++++++++---- .../workflow/hooks/use-nodes-interactions.ts | 35 ++++++++++----- web/app/components/workflow/index.tsx | 2 +- .../nodes/_base/components/node-handle.tsx | 24 +++++------ .../components/workflow/nodes/_base/node.tsx | 1 + .../workflow/operator/zoom-in-out.tsx | 5 ++- web/app/components/workflow/types.ts | 2 + web/app/components/workflow/utils.ts | 13 +++++- 9 files changed, 130 insertions(+), 36 deletions(-) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx index bb57d526c9..467d5be3c0 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx @@ -3,10 +3,51 @@ import { memo } from 'react' import Workflow from '@/app/components/workflow' +// export function createNodesAndEdges(xNodes = 10, yNodes = 10) { +// const nodes = [] +// const edges = [] +// let nodeId = 1 +// let recentNodeId = null + +// for (let y = 0; y < yNodes; y++) { +// for (let x = 0; x < xNodes; x++) { +// const position = { x: x * 200, y: y * 50 } +// const node = { +// id: `stress-${nodeId.toString()}`, +// type: 'custom', +// data: { type: 'start', title: '开始', variables: [] }, +// position, +// } +// nodes.push(node) + +// if (recentNodeId && nodeId <= xNodes * yNodes) { +// edges.push({ +// id: `${x}-${y}`, +// type: 'custom', +// source: `stress-${recentNodeId.toString()}`, +// target: `stress-${nodeId.toString()}`, +// }) +// } + +// recentNodeId = nodeId +// nodeId++ +// } +// } + +// return { nodes, edges } +// } + const Page = () => { + // const { + // nodes, + // edges, + // } = createNodesAndEdges() return (
- +
) } diff --git a/web/app/components/workflow/hooks/use-edges-interactions.ts b/web/app/components/workflow/hooks/use-edges-interactions.ts index 160d4367f4..20d5019707 100644 --- a/web/app/components/workflow/hooks/use-edges-interactions.ts +++ b/web/app/components/workflow/hooks/use-edges-interactions.ts @@ -6,6 +6,7 @@ import type { } from 'reactflow' import { useStoreApi } from 'reactflow' import { useStore } from '../store' +import type { Node } from '../types' import { useNodesSyncDraft } from './use-nodes-sync-draft' export const useEdgesInteractions = () => { @@ -55,14 +56,26 @@ export const useEdgesInteractions = () => { return const { + getNodes, + setNodes, edges, setEdges, } = store.getState() - const newEdges = produce(edges, (draft) => { - const index = draft.findIndex(edge => edge.source === nodeId && edge.sourceHandle === branchId) + const currentEdgeIndex = edges.findIndex(edge => edge.source === nodeId && edge.sourceHandle === branchId) + const currentEdge = edges[currentEdgeIndex] + const newNodes = produce(getNodes(), (draft: Node[]) => { + const sourceNode = draft.find(node => node.id === currentEdge.source) + const targetNode = draft.find(node => node.id === currentEdge.target) - if (index > -1) - draft.splice(index, 1) + if (sourceNode) + sourceNode.data._connectedSourceHandleIds = sourceNode.data._connectedSourceHandleIds?.filter(handleId => handleId !== currentEdge.sourceHandle) + + if (targetNode) + targetNode.data._connectedTargetHandleIds = targetNode.data._connectedTargetHandleIds?.filter(handleId => handleId !== currentEdge.targetHandle) + }) + setNodes(newNodes) + const newEdges = produce(edges, (draft) => { + draft.splice(currentEdgeIndex, 1) }) setEdges(newEdges) handleSyncWorkflowDraft() @@ -75,14 +88,26 @@ export const useEdgesInteractions = () => { return const { + getNodes, + setNodes, edges, setEdges, } = store.getState() - const newEdges = produce(edges, (draft) => { - const index = draft.findIndex(edge => edge.selected) + const currentEdgeIndex = edges.findIndex(edge => edge.selected) + const currentEdge = edges[currentEdgeIndex] + const newNodes = produce(getNodes(), (draft: Node[]) => { + const sourceNode = draft.find(node => node.id === currentEdge.source) + const targetNode = draft.find(node => node.id === currentEdge.target) - if (index > -1) - draft.splice(index, 1) + if (sourceNode) + sourceNode.data._connectedSourceHandleIds = sourceNode.data._connectedSourceHandleIds?.filter(handleId => handleId !== currentEdge.sourceHandle) + + if (targetNode) + targetNode.data._connectedTargetHandleIds = targetNode.data._connectedTargetHandleIds?.filter(handleId => handleId !== currentEdge.targetHandle) + }) + setNodes(newNodes) + const newEdges = produce(edges, (draft) => { + draft.splice(currentEdgeIndex, 1) }) setEdges(newEdges) handleSyncWorkflowDraft() diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index 02dd1c3d56..1fb90fa74b 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -260,9 +260,8 @@ export const useNodesInteractions = () => { edges, setEdges, } = store.getState() - const newEdges = produce(edges, (draft) => { - const filtered = draft.filter(edge => edge.source !== source && edge.target !== target) + const filtered = draft.filter(edge => (edge.source !== source && edge.sourceHandle !== sourceHandle) || (edge.target !== target && edge.targetHandle !== targetHandle)) filtered.push({ id: `${source}-${target}`, @@ -292,14 +291,25 @@ export const useNodesInteractions = () => { setEdges, } = store.getState() - const newNodes = produce(getNodes(), (draft) => { - const index = draft.findIndex(node => node.id === nodeId) + const nodes = getNodes() + const currentNodeIndex = nodes.findIndex(node => node.id === nodeId) + const currentNode = nodes[currentNodeIndex] + const incomersIds = getIncomers(currentNode, nodes, edges).map(incomer => incomer.id) + const outgoersIds = getOutgoers(currentNode, nodes, edges).map(outgoer => outgoer.id) + const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges) + const sourceEdgesHandleIds = connectedEdges.filter(edge => edge.target === nodeId).map(edge => edge.sourceHandle) + const targetEdgesHandleIds = connectedEdges.filter(edge => edge.source === nodeId).map(edge => edge.targetHandle) + const newNodes = produce(nodes, (draft: Node[]) => { + draft.forEach((node) => { + if (incomersIds.includes(node.id)) + node.data._connectedSourceHandleIds = node.data._connectedSourceHandleIds?.filter(handleId => !sourceEdgesHandleIds.includes(handleId)) - if (index > -1) - draft.splice(index, 1) + if (outgoersIds.includes(node.id)) + node.data._connectedTargetHandleIds = node.data._connectedTargetHandleIds?.filter(handleId => !targetEdgesHandleIds.includes(handleId)) + }) + draft.splice(currentNodeIndex, 1) }) setNodes(newNodes) - const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges) const newEdges = produce(edges, (draft) => { return draft.filter(edge => !connectedEdges.find(connectedEdge => connectedEdge.id === edge.id)) }) @@ -325,7 +335,8 @@ export const useNodesInteractions = () => { setEdges, } = store.getState() const nodes = getNodes() - const currentNode = nodes.find(node => node.id === currentNodeId)! + const currentNodeIndex = nodes.findIndex(node => node.id === currentNodeId) + const currentNode = nodes[currentNodeIndex] const outgoers = getOutgoers(currentNode, nodes, edges).sort((a, b) => a.position.y - b.position.y) const lastOutgoer = outgoers[outgoers.length - 1] const nextNode: Node = { @@ -334,6 +345,7 @@ export const useNodesInteractions = () => { data: { ...nodesInitialData[nodeType], ...(toolDefaultValue || {}), + _connectedTargetHandleIds: ['target'], selected: true, }, position: { @@ -350,9 +362,12 @@ export const useNodesInteractions = () => { target: nextNode.id, targetHandle: 'target', } - const newNodes = produce(nodes, (draft) => { - draft.forEach((node) => { + const newNodes = produce(nodes, (draft: Node[]) => { + draft.forEach((node, index) => { node.data.selected = false + + if (index === currentNodeIndex) + node.data._connectedSourceHandleIds?.push(sourceHandle) }) draft.push(nextNode) }) diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 513c7c4391..c43b2b2474 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -182,7 +182,7 @@ const WorkflowWrap: FC = ({ return nodes if (data) - return initialNodes(data.graph.nodes) + return initialNodes(data.graph.nodes, data.graph.edges) return [startNode] }, [data, nodes, startNode]) diff --git a/web/app/components/workflow/nodes/_base/components/node-handle.tsx b/web/app/components/workflow/nodes/_base/components/node-handle.tsx index 5803ae8ec4..f2955c8932 100644 --- a/web/app/components/workflow/nodes/_base/components/node-handle.tsx +++ b/web/app/components/workflow/nodes/_base/components/node-handle.tsx @@ -1,15 +1,13 @@ import type { MouseEvent } from 'react' import { + memo, useCallback, useEffect, useState, } from 'react' -import type { NodeProps } from 'reactflow' import { Handle, Position, - getConnectedEdges, - useEdges, } from 'reactflow' import { BlockEnum } from '../../../types' import type { Node } from '../../../types' @@ -22,9 +20,9 @@ type NodeHandleProps = { handleId: string handleClassName?: string nodeSelectorClassName?: string -} & Pick +} & Pick -export const NodeTargetHandle = ({ +export const NodeTargetHandle = memo(({ id, data, handleId, @@ -33,9 +31,7 @@ export const NodeTargetHandle = ({ }: NodeHandleProps) => { const [open, setOpen] = useState(false) const { handleNodeAddPrev } = useNodesInteractions() - const edges = useEdges() - const connectedEdges = getConnectedEdges([{ id } as Node], edges) - const connected = connectedEdges.find(edge => edge.targetHandle === handleId && edge.target === id) + const connected = data._connectedTargetHandleIds?.includes(handleId) const handleOpenChange = useCallback((v: boolean) => { setOpen(v) @@ -85,9 +81,10 @@ export const NodeTargetHandle = ({ ) -} +}) +NodeTargetHandle.displayName = 'NodeTargetHandle' -export const NodeSourceHandle = ({ +export const NodeSourceHandle = memo(({ id, data, handleId, @@ -97,9 +94,7 @@ export const NodeSourceHandle = ({ const notInitialWorkflow = useStore(s => s.notInitialWorkflow) const [open, setOpen] = useState(false) const { handleNodeAddNext } = useNodesInteractions() - const edges = useEdges() - const connectedEdges = getConnectedEdges([{ id } as Node], edges) - const connected = connectedEdges.find(edge => edge.sourceHandle === handleId && edge.source === id) + const connected = data._connectedSourceHandleIds?.includes(handleId) const handleOpenChange = useCallback((v: boolean) => { setOpen(v) }, []) @@ -150,4 +145,5 @@ export const NodeSourceHandle = ({ ) -} +}) +NodeSourceHandle.displayName = 'NodeSourceHandle' diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx index fd781bc64e..6a8079e560 100644 --- a/web/app/components/workflow/nodes/_base/node.tsx +++ b/web/app/components/workflow/nodes/_base/node.tsx @@ -47,6 +47,7 @@ const BaseNode: FC = ({ ${data._runningStatus === NodeRunningStatus.Running && '!border-primary-500'} ${data._runningStatus === NodeRunningStatus.Succeeded && '!border-[#12B76A]'} ${data._runningStatus === NodeRunningStatus.Failed && '!border-[#F04438]'} + ${data._runningStatus === NodeRunningStatus.Waiting && 'opacity-70'} `} > { diff --git a/web/app/components/workflow/operator/zoom-in-out.tsx b/web/app/components/workflow/operator/zoom-in-out.tsx index 8d732df726..0a4f09ae39 100644 --- a/web/app/components/workflow/operator/zoom-in-out.tsx +++ b/web/app/components/workflow/operator/zoom-in-out.tsx @@ -94,7 +94,10 @@ const ZoomInOut: FC = () => { placement='top-start' open={open} onOpenChange={setOpen} - offset={4} + offset={{ + mainAxis: 4, + crossAxis: -2, + }} >
= { + _connectedSourceHandleIds?: string[] + _connectedTargetHandleIds?: string[] _targetBranches?: Branch[] _isSingleRun?: boolean _runningStatus?: NodeRunningStatus diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts index 7939a5687d..0e0161ea19 100644 --- a/web/app/components/workflow/utils.ts +++ b/web/app/components/workflow/utils.ts @@ -80,10 +80,14 @@ export const nodesLevelOrderTraverse = ( } } -export const initialNodes = (nodes: Node[]) => { +export const initialNodes = (nodes: Node[], edges: Edge[]) => { return nodes.map((node) => { node.type = 'custom' + const connectedEdges = getConnectedEdges([node], edges) + node.data._connectedSourceHandleIds = connectedEdges.filter(edge => edge.source === node.id).map(edge => edge.sourceHandle || 'source') + node.data._connectedTargetHandleIds = connectedEdges.filter(edge => edge.target === node.id).map(edge => edge.targetHandle || 'target') + if (node.data.type === BlockEnum.IfElse) { node.data._targetBranches = [ { @@ -170,3 +174,10 @@ export const canRunBySingle = (nodeType: BlockEnum) => { || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Tool } + +type ConnectedSourceOrTargetNodesChange = { + type: 'add' | 'remove' + edge: Edge +}[] +export const getConnectedSourceOrTargetNodesChangeList = (changes: ConnectedSourceOrTargetNodesChange, nodes: Node[], edges: Edge[]) => { +}