Feat/loop break node (#17268)

This commit is contained in:
zxhlyh
2025-04-01 16:52:07 +08:00
committed by GitHub
parent 627a9e2ce1
commit 713902dc47
64 changed files with 1397 additions and 139 deletions

View File

@@ -42,6 +42,7 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, isInIteration?: boolean
const availableNextBlocks = useMemo(() => {
if (!nodeType)
return []
return nodesExtraData[nodeType].availableNextNodes || []
}, [nodeType, nodesExtraData])
@@ -54,6 +55,9 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, isInIteration?: boolean
if (isInLoop && (nType === BlockEnum.Iteration || nType === BlockEnum.Loop || nType === BlockEnum.End))
return false
if (!isInLoop && nType === BlockEnum.LoopEnd)
return false
return true
}),
availableNextBlocks: availableNextBlocks.filter((nType) => {
@@ -63,6 +67,9 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, isInIteration?: boolean
if (isInLoop && (nType === BlockEnum.Iteration || nType === BlockEnum.Loop || nType === BlockEnum.End))
return false
if (!isInLoop && nType === BlockEnum.LoopEnd)
return false
return true
}),
}

View File

@@ -39,6 +39,7 @@ import {
import {
genNewNodeTitleFromOld,
generateNewNode,
getNodeCustomTypeByNodeDataType,
getNodesConnectedSourceOrTargetHandleIdsMap,
getTopLeftNodePosition,
} from '../utils'
@@ -638,7 +639,7 @@ export const useNodesInteractions = () => {
}
if (node.id === currentNode.parentId)
node.data._children = node.data._children?.filter(child => child !== nodeId)
node.data._children = node.data._children?.filter(child => child.nodeId !== nodeId)
})
draft.splice(currentNodeIndex, 1)
})
@@ -686,6 +687,7 @@ export const useNodesInteractions = () => {
newIterationStartNode,
newLoopStartNode,
} = generateNewNode({
type: getNodeCustomTypeByNodeDataType(nodeType),
data: {
...NODES_INITIAL_DATA[nodeType],
title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${nodeType}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${nodeType}`),
@@ -775,10 +777,10 @@ export const useNodesInteractions = () => {
}
if (node.data.type === BlockEnum.Iteration && prevNode.parentId === node.id)
node.data._children?.push(newNode.id)
node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
if (node.data.type === BlockEnum.Loop && prevNode.parentId === node.id)
node.data._children?.push(newNode.id)
node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
})
draft.push(newNode)
@@ -853,7 +855,7 @@ export const useNodesInteractions = () => {
let newEdge
if ((nodeType !== BlockEnum.IfElse) && (nodeType !== BlockEnum.QuestionClassifier)) {
if ((nodeType !== BlockEnum.IfElse) && (nodeType !== BlockEnum.QuestionClassifier) && (nodeType !== BlockEnum.LoopEnd)) {
newEdge = {
id: `${newNode.id}-${sourceHandle}-${nextNodeId}-${nextNodeTargetHandle}`,
type: CUSTOM_EDGE,
@@ -901,7 +903,7 @@ export const useNodesInteractions = () => {
}
if (node.data.type === BlockEnum.Iteration && nextNode.parentId === node.id)
node.data._children?.push(newNode.id)
node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
if (node.data.type === BlockEnum.Iteration && node.data.start_node_id === nextNodeId) {
node.data.start_node_id = newNode.id
@@ -909,7 +911,7 @@ export const useNodesInteractions = () => {
}
if (node.data.type === BlockEnum.Loop && nextNode.parentId === node.id)
node.data._children?.push(newNode.id)
node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
if (node.data.type === BlockEnum.Loop && node.data.start_node_id === nextNodeId) {
node.data.start_node_id = newNode.id
@@ -1004,7 +1006,7 @@ export const useNodesInteractions = () => {
const isNextNodeInIteration = !!nextNodeParentNode && nextNodeParentNode.data.type === BlockEnum.Iteration
const isNextNodeInLoop = !!nextNodeParentNode && nextNodeParentNode.data.type === BlockEnum.Loop
if (nodeType !== BlockEnum.IfElse && nodeType !== BlockEnum.QuestionClassifier) {
if (nodeType !== BlockEnum.IfElse && nodeType !== BlockEnum.QuestionClassifier && nodeType !== BlockEnum.LoopEnd) {
newNextEdge = {
id: `${newNode.id}-${sourceHandle}-${nextNodeId}-${nextNodeTargetHandle}`,
type: CUSTOM_EDGE,
@@ -1049,9 +1051,9 @@ export const useNodesInteractions = () => {
node.position.x += NODE_WIDTH_X_OFFSET
if (node.data.type === BlockEnum.Iteration && prevNode.parentId === node.id)
node.data._children?.push(newNode.id)
node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
if (node.data.type === BlockEnum.Loop && prevNode.parentId === node.id)
node.data._children?.push(newNode.id)
node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
})
draft.push(newNode)
if (newIterationStartNode)
@@ -1117,6 +1119,7 @@ export const useNodesInteractions = () => {
newIterationStartNode,
newLoopStartNode,
} = generateNewNode({
type: getNodeCustomTypeByNodeDataType(nodeType),
data: {
...NODES_INITIAL_DATA[nodeType],
title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${nodeType}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${nodeType}`),
@@ -1240,7 +1243,7 @@ export const useNodesInteractions = () => {
if (nodeId) {
// If nodeId is provided, copy that specific node
const nodeToCopy = nodes.find(node => node.id === nodeId && node.data.type !== BlockEnum.Start
&& node.type !== CUSTOM_ITERATION_START_NODE && node.type !== CUSTOM_LOOP_START_NODE)
&& node.type !== CUSTOM_ITERATION_START_NODE && node.type !== CUSTOM_LOOP_START_NODE && node.data.type !== BlockEnum.LoopEnd)
if (nodeToCopy)
setClipboardElements([nodeToCopy])
}
@@ -1254,7 +1257,7 @@ export const useNodesInteractions = () => {
return
}
const selectedNode = nodes.find(node => node.data.selected && node.data.type !== BlockEnum.Start)
const selectedNode = nodes.find(node => node.data.selected && node.data.type !== BlockEnum.Start && node.data.type !== BlockEnum.LoopEnd)
if (selectedNode)
setClipboardElements([selectedNode])
@@ -1328,7 +1331,7 @@ export const useNodesInteractions = () => {
newChildren = copyChildren
idMapping = newIdMapping
newChildren.forEach((child) => {
newNode.data._children?.push(child.id)
newNode.data._children?.push({ nodeId: child.id, nodeType: child.data.type })
})
newChildren.push(newIterationStartNode!)
}
@@ -1339,7 +1342,7 @@ export const useNodesInteractions = () => {
newChildren = handleNodeLoopChildrenCopy(nodeToPaste.id, newNode.id)
newChildren.forEach((child) => {
newNode.data._children?.push(child.id)
newNode.data._children?.push({ nodeId: child.id, nodeType: child.data.type })
})
newChildren.push(newLoopStartNode!)
}
@@ -1424,7 +1427,7 @@ export const useNodesInteractions = () => {
const nodes = getNodes()
const currentNode = nodes.find(n => n.id === nodeId)!
const childrenNodes = nodes.filter(n => currentNode.data._children?.includes(n.id))
const childrenNodes = nodes.filter(n => currentNode.data._children?.find((c: any) => c.nodeId === n.id))
let rightNode: Node
let bottomNode: Node

View File

@@ -19,6 +19,8 @@ export const useWorkflowNodeIterationFinished = () => {
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
const nodes = getNodes()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
@@ -38,6 +40,18 @@ export const useWorkflowNodeIterationFinished = () => {
currentNode.data._runningStatus = data.status
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const incomeEdges = draft.filter((edge) => {
return edge.target === data.node_id
})
incomeEdges.forEach((edge) => {
edge.data = {
...edge.data,
_targetRunningStatus: data.status as any,
}
})
})
setEdges(newEdges)
}, [workflowStore, store])
return {

View File

@@ -3,7 +3,6 @@ import { useStoreApi } from 'reactflow'
import produce from 'immer'
import type { LoopFinishedResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { DEFAULT_LOOP_TIMES } from '@/app/components/workflow/constants'
export const useWorkflowNodeLoopFinished = () => {
const store = useStoreApi()
@@ -14,11 +13,12 @@ export const useWorkflowNodeLoopFinished = () => {
const {
workflowRunningData,
setWorkflowRunningData,
setLoopTimes,
} = workflowStore.getState()
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
const nodes = getNodes()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
@@ -31,13 +31,24 @@ export const useWorkflowNodeLoopFinished = () => {
}
}
}))
setLoopTimes(DEFAULT_LOOP_TIMES)
const newNodes = produce(nodes, (draft) => {
const currentNode = draft.find(node => node.id === data.node_id)!
currentNode.data._runningStatus = data.status
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const incomeEdges = draft.filter((edge) => {
return edge.target === data.node_id
})
incomeEdges.forEach((edge) => {
edge.data = {
...edge.data,
_targetRunningStatus: data.status as any,
}
})
})
setEdges(newEdges)
}, [workflowStore, store])
return {

View File

@@ -2,18 +2,12 @@ import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import produce from 'immer'
import type { LoopNextResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { NodeRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowNodeLoopNext = () => {
const store = useStoreApi()
const workflowStore = useWorkflowStore()
const handleWorkflowNodeLoopNext = useCallback((params: LoopNextResponse) => {
const {
loopTimes,
setLoopTimes,
} = workflowStore.getState()
const { data } = params
const {
getNodes,
@@ -23,11 +17,17 @@ export const useWorkflowNodeLoopNext = () => {
const nodes = getNodes()
const newNodes = produce(nodes, (draft) => {
const currentNode = draft.find(node => node.id === data.node_id)!
currentNode.data._loopIndex = loopTimes
setLoopTimes(loopTimes + 1)
currentNode.data._loopIndex = data.index
draft.forEach((node) => {
if (node.parentId === data.node_id) {
node.data._waitingRun = true
node.data._runningStatus = NodeRunningStatus.Waiting
}
})
})
setNodes(newNodes)
}, [workflowStore, store])
}, [store])
return {
handleWorkflowNodeLoopNext,

View File

@@ -7,7 +7,6 @@ import produce from 'immer'
import { useWorkflowStore } from '@/app/components/workflow/store'
import type { LoopStartedResponse } from '@/types/workflow'
import { NodeRunningStatus } from '@/app/components/workflow/types'
import { DEFAULT_LOOP_TIMES } from '@/app/components/workflow/constants'
export const useWorkflowNodeLoopStarted = () => {
const store = useStoreApi()
@@ -25,7 +24,6 @@ export const useWorkflowNodeLoopStarted = () => {
const {
workflowRunningData,
setWorkflowRunningData,
setLoopTimes,
} = workflowStore.getState()
const {
getNodes,
@@ -41,7 +39,6 @@ export const useWorkflowNodeLoopStarted = () => {
status: NodeRunningStatus.Running,
})
}))
setLoopTimes(DEFAULT_LOOP_TIMES)
const {
setViewport,