jiaxing.liao пре 1 месец
родитељ
комит
fadadf82c3

+ 97 - 11
apps/web/src/views/Editor.vue

@@ -81,7 +81,7 @@
 				<Setter
 					:id="nodeID"
 					:workflow="workflow!"
-					@update:node:data="handleUpdateNodeData"
+					@update:node:data="handleUpdateNode"
 					@run-node="handleRunNode"
 					v-model:visible="setterVisible"
 				/>
@@ -186,11 +186,96 @@ const mapNodeExecutionStatus = (status?: string): CanvasExecutionStatus => {
 	return 'idle'
 }
 
+const MIN_NODE_RUNNING_EFFECT_MS = 500
+const displayNodeExecutionStatus = ref<Record<string, CanvasExecutionStatus>>({})
+const runningStatusStartedAt = new Map<string, number>()
+const pendingNodeStatusTimers = new Map<string, number>()
+
+const clearPendingNodeStatusTimer = (nodeId: string) => {
+	const timer = pendingNodeStatusTimers.get(nodeId)
+	if (timer) {
+		window.clearTimeout(timer)
+		pendingNodeStatusTimers.delete(nodeId)
+	}
+}
+
+const applyDisplayedNodeStatus = (nodeId: string, status: CanvasExecutionStatus) => {
+	if (status === 'idle') {
+		delete displayNodeExecutionStatus.value[nodeId]
+		return
+	}
+
+	displayNodeExecutionStatus.value[nodeId] = status
+}
+
+const syncDisplayedNodeStatus = (nodeId: string, nextStatus: CanvasExecutionStatus) => {
+	const currentStatus = displayNodeExecutionStatus.value[nodeId] || 'idle'
+
+	if (nextStatus === 'running') {
+		clearPendingNodeStatusTimer(nodeId)
+		runningStatusStartedAt.set(nodeId, Date.now())
+		applyDisplayedNodeStatus(nodeId, 'running')
+		return
+	}
+
+	const runningStartedAt = runningStatusStartedAt.get(nodeId)
+	const shouldKeepRunning =
+		currentStatus === 'running' &&
+		typeof runningStartedAt === 'number' &&
+		Date.now() - runningStartedAt < MIN_NODE_RUNNING_EFFECT_MS
+
+	if (shouldKeepRunning) {
+		clearPendingNodeStatusTimer(nodeId)
+		const delay = MIN_NODE_RUNNING_EFFECT_MS - (Date.now() - runningStartedAt)
+		const timer = window.setTimeout(() => {
+			applyDisplayedNodeStatus(nodeId, nextStatus)
+			runningStatusStartedAt.delete(nodeId)
+			pendingNodeStatusTimers.delete(nodeId)
+		}, delay)
+		pendingNodeStatusTimers.set(nodeId, timer)
+		return
+	}
+
+	clearPendingNodeStatusTimer(nodeId)
+	applyDisplayedNodeStatus(nodeId, nextStatus)
+	runningStatusStartedAt.delete(nodeId)
+}
+
+const resetDisplayedNodeStatuses = () => {
+	pendingNodeStatusTimers.forEach((timer) => window.clearTimeout(timer))
+	pendingNodeStatusTimers.clear()
+	runningStatusStartedAt.clear()
+	displayNodeExecutionStatus.value = {}
+}
+
+watch(
+	() => runnerStore.currentRunnerKey,
+	() => {
+		resetDisplayedNodeStatuses()
+	}
+)
+
+watch(
+	() => runnerStore.nodes.map((item) => ({ nodeId: item.nodeId, status: item.status })),
+	(nodeStates) => {
+		const activeNodeIds = new Set(nodeStates.map((item) => item.nodeId))
+
+		nodeStates.forEach((item) => {
+			syncDisplayedNodeStatus(item.nodeId, mapNodeExecutionStatus(item.status))
+		})
+
+		Object.keys(displayNodeExecutionStatus.value).forEach((nodeId) => {
+			if (!activeNodeIds.has(nodeId)) {
+				syncDisplayedNodeStatus(nodeId, 'idle')
+			}
+		})
+	},
+	{ immediate: true, deep: true }
+)
+
 const workflowWithExecutionState = computed<IWorkflow>(() => {
 	const baseWorkflow = workflow.value
-	const runnerNodeStatusMap = new Map(
-		runnerStore.nodes.map((item) => [item.nodeId, mapNodeExecutionStatus(item.status)])
-	)
+	const runnerNodeStatusMap = new Map(Object.entries(displayNodeExecutionStatus.value))
 
 	return {
 		...baseWorkflow,
@@ -603,9 +688,9 @@ const handleNodeCreate = (value: { type: string; position?: XYPosition } | strin
 			if (handle.handleId?.includes('_')) {
 				newNodeParam.nodeHandleId = handle.handleId
 			}
-			if (handle.handleId?.endsWith('-else')) {
-				newNodeParam.nodeHandleId = handle.nodeId
-			}
+			// if (handle.handleId?.endsWith('-else')) {
+			// 	newNodeParam.nodeHandleId = handle.nodeId
+			// }
 		}
 
 		if (!newNodeParam.position) {
@@ -692,9 +777,9 @@ const onCreateConnection = async (connection: Connection) => {
 	}
 
 	// 如果是source条件节点else的sourceHandle模式就是当前节点id
-	if (sourceHandle && sourceHandle.endsWith('-else')) {
-		params.sourceHandle = source
-	}
+	// if (sourceHandle && sourceHandle.endsWith('-else')) {
+	// 	params.sourceHandle = source
+	// }
 
 	if (!workflow.value?.edges.some((edge) => edge.source === source && edge.target === target)) {
 		const response = await agent.postAgentDoNewEdge(params)
@@ -746,7 +831,7 @@ const handleSelectNode = (id: string) => {
 /**
  * 修改节点数据
  */
-const hangleUpdateNodeData = (node: IWorkflowNode) => {
+const handleUpdateNode = (node: IWorkflowNode) => {
 	if (node && !isPendingCreate(node)) {
 		if (pendingSetterInit.has(id)) {
 			pendingSetterInit.delete(id)
@@ -881,6 +966,7 @@ const onHideNodeLibary = () => {
 onBeforeUnmount(() => {
 	if (saveAgentTimer.value) window.clearTimeout(saveAgentTimer.value)
 	if (saveVarsTimer.value) window.clearTimeout(saveVarsTimer.value)
+	resetDisplayedNodeStatuses()
 	layout?.setMainStyle({})
 })
 </script>

+ 1 - 1
packages/workflow/src/components/elements/edges/CanvasEdge.vue

@@ -37,7 +37,7 @@ const edgeStyle = computed(() => {
 
 	return {
 		stroke: color,
-		strokeWidth: status === 'idle' ? 1.6 : 2.5,
+		strokeWidth: 1.6,
 		filter: status === 'idle' ? 'none' : `drop-shadow(0 0 8px ${color}55)`,
 		transition: 'stroke 180ms ease, stroke-width 180ms ease, filter 180ms ease'
 	}