diff --git a/src/components/InstanceCanvas/index.tsx b/src/components/InstanceCanvas/index.tsx index 9f179ac..9279052 100644 --- a/src/components/InstanceCanvas/index.tsx +++ b/src/components/InstanceCanvas/index.tsx @@ -48,6 +48,7 @@ const InstanceCanvas: React.FC = ({ instanceData, title, on const [runtimeLogs, setRuntimeLogs] = useState([]); const [runtimeData, setRuntimeData] = useState({}); const [flowData, setFlowData] = useState(null); // 流程数据 + const [nodeStatusMap, setNodeStatusMap] = useState>({}); // 节点状态映射 const [loading, setLoading] = useState(false); // 加载状态 const resizeBoxRef = useRef(null); const flowDataRef = useRef(null); // 使用 ref 来存储 flowData,避免重复设置 @@ -111,6 +112,8 @@ const InstanceCanvas: React.FC = ({ instanceData, title, on } }; + console.log('InstanceCanvas - 转换后的 flowData:', newFlowData); + // 只在数据真正变化时才更新 if (!flowDataRef.current) { flowDataRef.current = newFlowData; @@ -118,7 +121,7 @@ const InstanceCanvas: React.FC = ({ instanceData, title, on } } - // 处理运行日志 + // 处理运行日志和节点状态 if (res.data.main?.nodeLogs && Array.isArray(res.data.main.nodeLogs) && res.data.main.nodeLogs.length > 0) { const logs = res.data.main.nodeLogs.map((log: any, index: number) => ({ id: index, @@ -131,6 +134,20 @@ const InstanceCanvas: React.FC = ({ instanceData, title, on state: log.state })); setRuntimeLogs(logs); + + // 构建节点状态映射 + const statusMap: Record = {}; + res.data.main.nodeLogs.forEach((log: any) => { + // state: 1=成功(success), 0=运行中(running), -1=失败(failed) + if (log.state === 1) { + statusMap[log.nodeId] = 'success'; + } else if (log.state === -1) { + statusMap[log.nodeId] = 'failed'; + } else if (log.state === 0) { + statusMap[log.nodeId] = 'running'; + } + }); + setNodeStatusMap(statusMap); } // 处理运行数据(input/output) @@ -198,6 +215,40 @@ const InstanceCanvas: React.FC = ({ instanceData, title, on } }, []); + // 使用 useMemo 创建带有节点状态的 flowData + const flowDataWithStatus = useMemo(() => { + if (!flowData || Object.keys(nodeStatusMap).length === 0) { + return flowData; + } + + console.log('InstanceCanvas - 注入状态前的 flowData:', flowData); + console.log('InstanceCanvas - 要注入的状态映射:', nodeStatusMap); + + // 深拷贝 flowData 并注入节点状态 + const componentsWithStatus = { ...flowData.components }; + Object.keys(nodeStatusMap).forEach((nodeId) => { + if (componentsWithStatus[nodeId]) { + componentsWithStatus[nodeId] = { + ...componentsWithStatus[nodeId], + status: nodeStatusMap[nodeId], + isStatusVisible: true // 显示状态指示器 + }; + } + }); + + const result = { + ...flowData, + components: componentsWithStatus, + main: { + ...flowData.main, + components: componentsWithStatus + } + }; + + console.log('InstanceCanvas - 注入状态后的 flowData:', result); + return result; + }, [flowData, nodeStatusMap]); + // 渲染运行日志内容 const renderRuntimeLogs = () => { return ( @@ -339,9 +390,9 @@ const InstanceCanvas: React.FC = ({ instanceData, title, on 加载中... - ) : flowData ? ( + ) : flowDataWithStatus ? ( ) : ( diff --git a/src/hooks/useFlowEditorState.ts b/src/hooks/useFlowEditorState.ts index fb9708f..fa91ae6 100644 --- a/src/hooks/useFlowEditorState.ts +++ b/src/hooks/useFlowEditorState.ts @@ -57,18 +57,28 @@ export const useFlowEditorState = (initialData?: any) => { ? appRuntimeData[currentAppKey].nodeStatusMap : {}; + // 检查 initialData 中是否包含节点状态(用于查看历史实例) + const hasInitialDataStatus = initialData?.components && + Object.values(initialData.components).some((comp: any) => comp.status); + setNodes(prevNodes =>{ - return prevNodes.map(node => ({ - ...node, - data: { - ...node.data, - status: currentNodeStatusMap[node.id] || 'waiting', - isStatusVisible: currentAppIsRunning // 只有在运行时才显示状态指示器 - } - })) + return prevNodes.map(node => { + // 优先使用运行时状态,其次使用节点自身的状态(历史实例),最后默认为 waiting + const nodeStatus = currentNodeStatusMap[node.id] || node.data.status || 'waiting'; + + return { + ...node, + data: { + ...node.data, + status: nodeStatus, + // 在运行时或有历史状态时显示状态指示器 + isStatusVisible: currentAppIsRunning || hasInitialDataStatus || node.data.isStatusVisible + } + }; + }); }); - }, [appRuntimeData, currentAppKey, currentAppIsRunning, initialData?.id,nodes.length]); + }, [appRuntimeData, currentAppKey, currentAppIsRunning, initialData?.id, initialData?.components, nodes.length]); const updateCanvasDataMapDebounced = useRef( debounce((dispatch: Dispatch, canvasDataMap: any, id: string, nodes: Node[], edges: Edge[]) => { diff --git a/src/utils/convertFlowData.ts b/src/utils/convertFlowData.ts index b70ad23..be09144 100644 --- a/src/utils/convertFlowData.ts +++ b/src/utils/convertFlowData.ts @@ -147,6 +147,16 @@ export const convertFlowData = (flowData: any, useDefault = true) => { node.data.compId = nodeConfig.component.compId; } + // 保留节点状态信息(用于历史实例查看) + if (nodeConfig.status) { + node.data.status = nodeConfig.status; + console.log(`convertFlowData - 保留节点 ${nodeId} 的状态:`, nodeConfig.status); + } + if (nodeConfig.isStatusVisible !== undefined) { + node.data.isStatusVisible = nodeConfig.isStatusVisible; + console.log(`convertFlowData - 保留节点 ${nodeId} 的状态可见性:`, nodeConfig.isStatusVisible); + } + // 注册循环节点类型 if (nodeType === 'LOOP') { const nodeMap = Array.from(Object.values(nodeTypeMap).map(key => key)); @@ -379,6 +389,8 @@ export const convertFlowData = (flowData: any, useDefault = true) => { }); } } + + console.log('nodes, edges:', nodes, edges); return { nodes, edges }; }; @@ -675,12 +687,15 @@ const getNodeApiIns = (nodeId: string, nodeConfig: any, currentProjectCompData: else if (nodeId.includes('end')) { return [{ name: 'end', desc: '', dataType: '', defaultValue: '' }]; } + else if (nodeConfig.component?.type === 'SUB') { + return [{ name: 'start', desc: '', dataType: '', defaultValue: '' }]; + } else { const comp = currentProjectCompData.filter(item => { return (item.id || item?.comp?.id) === nodeConfig?.component?.compId; }); if (comp && comp.length > 0) { - const apiIns = comp[0].def?.apis || comp[0]?.comp.def?.apis; + const apiIns = comp[0]?.def?.apis || comp[0]?.comp?.def?.apis || []; return apiIns.map(v => { return { ...v, @@ -768,6 +783,9 @@ const getNodeApiOuts = (nodeId: string, nodeConfig: any, currentProjectCompData: else if (nodeId.includes('end')) { return []; } + else if (nodeConfig.component?.type === 'SUB') { + return [{ name: 'done', desc: '', dataType: '', defaultValue: '' }]; + } else { const comp = currentProjectCompData.filter(item => item.id === nodeConfig?.component?.compId); if (comp && comp.length > 0) {