feat(flow): 添加节点运行状态可见性控制

master
钟良源 4 months ago
parent b9502164b2
commit c13e0c1619

@ -1,12 +1,16 @@
import React from 'react'; import React from 'react';
import { NodeProps, useStore } from '@xyflow/react';
import styles from './node/style/baseOther.module.less'; import styles from './node/style/baseOther.module.less';
// 定义节点状态类型 // 定义节点状态类型
export type NodeStatus = 'waiting' | 'running' | 'success' | 'failed'; export type NodeStatus = 'waiting' | 'running' | 'success' | 'failed';
// 节点状态指示器组件 // 节点状态指示器组件
const NodeStatusIndicator: React.FC<{ status: NodeStatus }> = ({ status }) => { const NodeStatusIndicator: React.FC<{ status: NodeStatus, isVisible: boolean }> = ({ status, isVisible }) => {
// 如果不可见,不渲染任何内容
if (!isVisible) {
return null;
}
// 根据状态返回相应的指示器样式 // 根据状态返回相应的指示器样式
const getStatusIndicator = () => { const getStatusIndicator = () => {
switch (status) { switch (status) {

@ -17,12 +17,17 @@ const AppNode = ({ data, id }: { data: any; id: string }) => {
const nodeStatus: NodeStatus = useFlowStore((state) => const nodeStatus: NodeStatus = useFlowStore((state) =>
(state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting' (state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting'
); );
// 获取运行状态可见性
const isStatusVisible = useFlowStore((state) =>
!!state.nodeLookup.get(id)?.data?.isStatusVisible
);
return ( return (
<div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}> <div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}>
<div className={styles['node-header']} style={{ backgroundColor: '#722ed1' }}> <div className={styles['node-header']} style={{ backgroundColor: '#722ed1' }}>
{title} {title}
<NodeStatusIndicator status={nodeStatus} /> <NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} />
</div> </div>
<NodeContent data={data} /> <NodeContent data={data} />
</div> </div>

@ -14,15 +14,20 @@ const BasicNode = ({ data, id }: { data: any; id: string }) => {
); );
// 获取节点运行状态 // 获取节点运行状态
const nodeStatus: NodeStatus = useFlowStore((state) => const nodeStatus: NodeStatus = useFlowStore((state) =>
(state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting' (state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting'
); );
// 获取运行状态可见性
const isStatusVisible = useFlowStore((state) =>
!!state.nodeLookup.get(id)?.data?.isStatusVisible
);
return ( return (
<div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}> <div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}>
<div className={styles['node-header']} style={{ backgroundColor: '#e59428' }}> <div className={styles['node-header']} style={{ backgroundColor: '#e59428' }}>
{title} {title}
<NodeStatusIndicator status={nodeStatus} /> <NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} />
</div> </div>
<NodeContentOther data={{ ...data, type: 'basic' }} /> <NodeContentOther data={{ ...data, type: 'basic' }} />
</div> </div>

@ -18,12 +18,17 @@ const EndNode = ({ data, id }: { data: defaultNodeTypes; id: string }) => {
const nodeStatus: NodeStatus = useFlowStore((state) => const nodeStatus: NodeStatus = useFlowStore((state) =>
(state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting' (state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting'
); );
// 获取运行状态可见性
const isStatusVisible = useFlowStore((state) =>
!!state.nodeLookup.get(id)?.data?.isStatusVisible
);
return ( return (
<div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}> <div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}>
<div className={styles['node-header']} style={{ backgroundColor: '#c05144' }}> <div className={styles['node-header']} style={{ backgroundColor: '#c05144' }}>
{title} {title}
<NodeStatusIndicator status={nodeStatus} /> <NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} />
</div> </div>
<NodeContentOther data={{ ...data, type: 'end' }} /> <NodeContentOther data={{ ...data, type: 'end' }} />
</div> </div>

@ -70,13 +70,18 @@ const LocalNode = ({ data, id }: { data: any; id: string }) => {
const nodeStatus: NodeStatus = useFlowStore((state) => const nodeStatus: NodeStatus = useFlowStore((state) =>
(state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting' (state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting'
); );
// 获取运行状态可见性
const isStatusVisible = useFlowStore((state) =>
!!state.nodeLookup.get(id)?.data?.isStatusVisible
);
return ( return (
<div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}> <div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}>
<div className={styles['node-header']} style={{ backgroundColor: '#1890ff' }}> <div className={styles['node-header']} style={{ backgroundColor: '#1890ff' }}>
{setIcon(data.type)} {setIcon(data.type)}
{title} {title}
<NodeStatusIndicator status={nodeStatus} /> <NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} />
</div> </div>
<NodeContentOther data={data} /> <NodeContentOther data={data} />
</div> </div>

@ -18,12 +18,17 @@ const StartNode = ({ data, id }: { data: defaultNodeTypes; id: string }) => {
const nodeStatus: NodeStatus = useFlowStore((state) => const nodeStatus: NodeStatus = useFlowStore((state) =>
(state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting' (state.nodeLookup.get(id)?.data?.status as NodeStatus) || 'waiting'
); );
// 获取运行状态可见性
const isStatusVisible = useFlowStore((state) =>
!!state.nodeLookup.get(id)?.data?.isStatusVisible
);
return ( return (
<div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}> <div className={`${styles['node-container']} ${isSelected ? styles.selected : ''}`}>
<div className={styles['node-header']} style={{ backgroundColor: '#29b971' }}> <div className={styles['node-header']} style={{ backgroundColor: '#29b971' }}>
{title} {title}
<NodeStatusIndicator status={nodeStatus} /> <NodeStatusIndicator status={nodeStatus} isVisible={isStatusVisible} />
</div> </div>
<NodeContentOther data={{ ...data, type: 'start' }} /> <NodeContentOther data={{ ...data, type: 'start' }} />
</div> </div>

@ -15,7 +15,7 @@ import { localNodeData } from '@/pages/flowEditor/sideBar/config/localNodeData';
import { useAlignmentGuidelines } from '@/hooks/useAlignmentGuidelines'; import { useAlignmentGuidelines } from '@/hooks/useAlignmentGuidelines';
import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode'; import LocalNode from '@/components/FlowEditor/node/localNode/LocalNode';
import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode'; import LoopNode from '@/components/FlowEditor/node/loopNode/LoopNode';
import { updateCanvasDataMap, resetNodeStatus } from '@/store/ideContainer'; import { updateCanvasDataMap, resetNodeStatus, updateIsRunning } from '@/store/ideContainer';
import { import {
validateAllNodes, validateAllNodes,
showValidationErrors, showValidationErrors,
@ -919,13 +919,36 @@ export const useFlowCallbacks = (
socketId socketId
}; };
runMainFlow(params); runMainFlow(params);
// 设置运行状态为true
dispatch(updateIsRunning(true));
// 重置节点状态 // 重置节点状态
dispatch(resetNodeStatus()); dispatch(resetNodeStatus());
// 开始运行时动画
setEdges((eds) => eds.map(edge => ({
...edge,
data: {
...edge.data,
isRunning: true,
animationProgress: 0
}
})));
} }
else { else {
// 设置运行状态为false
dispatch(updateIsRunning(false));
// 停止运行 // 停止运行
setEdges((eds) => eds.map(edge => ({
...edge,
data: {
...edge.data,
isRunning: false,
animationProgress: 0
}
})));
} }
}, [initialData?.appId]); }, [initialData?.appId]);

@ -9,7 +9,7 @@ import { Dispatch } from 'redux';
export const useFlowEditorState = (initialData?: any) => { export const useFlowEditorState = (initialData?: any) => {
const [nodes, setNodes] = useState<Node[]>([]); const [nodes, setNodes] = useState<Node[]>([]);
const [edges, setEdges] = useState<Edge[]>([]); const [edges, setEdges] = useState<Edge[]>([]);
const { canvasDataMap, nodeStatusMap } = useSelector((state: any) => state.ideContainer); const { canvasDataMap, nodeStatusMap, isRunning } = useSelector((state: any) => state.ideContainer);
const dispatch = useDispatch(); const dispatch = useDispatch();
// 添加编辑弹窗相关状态 // 添加编辑弹窗相关状态
@ -21,9 +21,6 @@ export const useFlowEditorState = (initialData?: any) => {
const [edgeForNodeAdd, setEdgeForNodeAdd] = useState<Edge | null>(null); const [edgeForNodeAdd, setEdgeForNodeAdd] = useState<Edge | null>(null);
const [positionForNodeAdd, setPositionForNodeAdd] = useState<{ x: number, y: number } | null>(null); const [positionForNodeAdd, setPositionForNodeAdd] = useState<{ x: number, y: number } | null>(null);
// 添加运行状态
const [isRunning, setIsRunning] = useState(false);
// 在组件顶部添加历史记录相关状态 // 在组件顶部添加历史记录相关状态
const [historyInitialized, setHistoryInitialized] = useState(false); const [historyInitialized, setHistoryInitialized] = useState(false);
const historyTimeoutRef = useRef<NodeJS.Timeout | null>(null); const historyTimeoutRef = useRef<NodeJS.Timeout | null>(null);
@ -35,11 +32,12 @@ export const useFlowEditorState = (initialData?: any) => {
...node, ...node,
data: { data: {
...node.data, ...node.data,
status: nodeStatusMap[node.id] || 'waiting' status: nodeStatusMap[node.id] || 'waiting',
isStatusVisible: isRunning // 只有在运行时才显示状态指示器
} }
})) }))
); );
}, [nodeStatusMap]); }, [nodeStatusMap, isRunning]);
const updateCanvasDataMapDebounced = useRef( const updateCanvasDataMapDebounced = useRef(
debounce((dispatch: Dispatch<any>, canvasDataMap: any, id: string, nodes: Node[], edges: Edge[]) => { debounce((dispatch: Dispatch<any>, canvasDataMap: any, id: string, nodes: Node[], edges: Edge[]) => {
@ -68,7 +66,6 @@ export const useFlowEditorState = (initialData?: any) => {
positionForNodeAdd, positionForNodeAdd,
setPositionForNodeAdd, setPositionForNodeAdd,
isRunning, isRunning,
setIsRunning,
historyInitialized, historyInitialized,
setHistoryInitialized, setHistoryInitialized,
historyTimeoutRef, historyTimeoutRef,

@ -1,5 +1,6 @@
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
// 定义初始状态类型
interface IDEContainerState { interface IDEContainerState {
info: any; info: any;
menuData: any; menuData: any;
@ -11,8 +12,10 @@ interface IDEContainerState {
logBarStatus?: boolean; logBarStatus?: boolean;
socketId: string; socketId: string;
nodeStatusMap: Record<string, string>; // 节点状态映射 nodeStatusMap: Record<string, string>; // 节点状态映射
isRunning: boolean; // 是否正在运行
} }
// 初始状态
const initialState: IDEContainerState = { const initialState: IDEContainerState = {
info: {}, // 项目信息 info: {}, // 项目信息
menuData: {}, // 菜单数据 menuData: {}, // 菜单数据
@ -23,7 +26,8 @@ const initialState: IDEContainerState = {
eventList: [], // 工程下的事件列表 eventList: [], // 工程下的事件列表
logBarStatus: false, logBarStatus: false,
socketId: '', // 工程的socketId socketId: '', // 工程的socketId
nodeStatusMap: {} // 初始化节点状态映射 nodeStatusMap: {}, // 初始化节点状态映射
isRunning: false // 默认未运行
}; };
// 创建切片 // 创建切片
@ -66,6 +70,10 @@ const ideContainerSlice = createSlice({
// 重置节点状态 // 重置节点状态
resetNodeStatus: (state) => { resetNodeStatus: (state) => {
state.nodeStatusMap = {}; state.nodeStatusMap = {};
},
// 更新运行状态
updateIsRunning: (state, { payload }) => {
state.isRunning = payload;
} }
} }
}); });
@ -82,7 +90,8 @@ export const {
updateLogBarStatus, updateLogBarStatus,
updateSocketId, updateSocketId,
updateNodeStatus, updateNodeStatus,
resetNodeStatus resetNodeStatus,
updateIsRunning
} = ideContainerSlice.actions; } = ideContainerSlice.actions;
// 默认导出 reducer // 默认导出 reducer

Loading…
Cancel
Save