feat(flow): 支持子流程运行与状态管理

- 新增子流程运行逻辑,区分主流程与子流程的运行处理
- 统一使用 appKey 作为应用/子流程的唯一标识符
- 优化运行状态、日志、节点状态的存储与获取逻辑
- 改进画布数据初始化与更新逻辑,支持子流程场景
- 增强组件树结构展示,明确区分普通组件与复合组件
- 修复标签页切换及子流程打开的相关问题
- 优化日志面板对不同应用类型的支持
- 提取公共辅助函数 getCurrentAppKey 用于获取应用标识符
- 调整 Redux 状态更新逻辑以适配子流程场景
- 增强事件监听与状态同步机制,确保数据一致性
master
钟良源 2 months ago
parent 1b40b8c9c7
commit 3ec6d1700e

@ -39,7 +39,7 @@ import { appFLowHandle } from '@/pages/flowEditor/utils/appFlowhandle';
import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common'; import { handelEventNodeList, updateEvent, upDatePublish } from '@/pages/flowEditor/utils/common';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { getAppListBySceneId, runMainFlow, stopApp } from '@/api/apps'; import { getAppListBySceneId, runMainFlow, runSubFlow, stopApp } from '@/api/apps';
import store from '@/store'; import store from '@/store';
import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent'; import { updateAppEvent, updateAppEventChannel, updateAppFlowData } from '@/api/appEvent';
import { getUrlParams, sleep } from '@/utils/common'; import { getUrlParams, sleep } from '@/utils/common';
@ -74,6 +74,19 @@ export const useFlowCallbacks = (
setIsDelete: React.Dispatch<React.SetStateAction<boolean>> setIsDelete: React.Dispatch<React.SetStateAction<boolean>>
) => { ) => {
const { getGuidelines, clearGuidelines } = useAlignmentGuidelines(); const { getGuidelines, clearGuidelines } = useAlignmentGuidelines();
// 辅助函数:获取当前应用/子流程的唯一标识符
const getCurrentAppKey = useCallback(() => {
const { currentAppData } = store.getState().ideContainer;
if (!currentAppData) return null;
// 如果是子流程key包含'sub'使用key作为标识符
if (currentAppData.key && currentAppData.key.includes('sub')) {
return currentAppData.key;
}
// 否则使用id或initialData.appId
return currentAppData.id || initialData?.appId;
}, [initialData]);
// region 画布操作 // region 画布操作
// 节点变更处理,添加防抖机制 // 节点变更处理,添加防抖机制
const onNodesChange = useCallback((changes: any) => { const onNodesChange = useCallback((changes: any) => {
@ -448,8 +461,9 @@ export const useFlowCallbacks = (
// region 画布数据处理 // region 画布数据处理
// 初始化画布数据 // 初始化画布数据
const initializeCanvasData = useCallback(() => { const initializeCanvasData = useCallback(() => {
if (canvasDataMap[initialData?.appId]) { const appKey = getCurrentAppKey();
const { edges, nodes } = canvasDataMap[initialData?.appId]; if (appKey && canvasDataMap[appKey]) {
const { edges, nodes } = canvasDataMap[appKey];
setNodes(nodes); setNodes(nodes);
setEdges(edges); setEdges(edges);
} }
@ -461,18 +475,19 @@ export const useFlowCallbacks = (
// 标记历史记录已初始化 // 标记历史记录已初始化
setHistoryInitialized(true); setHistoryInitialized(true);
}, [initialData, canvasDataMap]); }, [initialData, canvasDataMap, getCurrentAppKey]);
// 实时更新 canvasDataMap // 实时更新 canvasDataMap
const updateCanvasDataMapEffect = useCallback(() => { const updateCanvasDataMapEffect = useCallback(() => {
if (initialData?.appId) { const appKey = getCurrentAppKey();
updateCanvasDataMapDebounced(dispatch, canvasDataMap, initialData.appId, nodes, edges); if (appKey) {
updateCanvasDataMapDebounced(dispatch, canvasDataMap, appKey, nodes, edges);
} }
// 清理函数,在组件卸载时取消防抖 // 清理函数,在组件卸载时取消防抖
return () => { return () => {
// 取消防抖函数 // 取消防抖函数
}; };
}, [nodes, edges, initialData?.appId, dispatch, canvasDataMap]); }, [nodes, edges, dispatch, canvasDataMap, getCurrentAppKey]);
// 关闭编辑弹窗 // 关闭编辑弹窗
const closeEditModal = useCallback(() => { const closeEditModal = useCallback(() => {
setIsEditModalOpen(false); setIsEditModalOpen(false);
@ -1152,14 +1167,19 @@ export const useFlowCallbacks = (
const appRes: any = await getAppInfoNew(currentAppData.parentAppId); const appRes: any = await getAppInfoNew(currentAppData.parentAppId);
// 更新 flowData 中的数据 // 更新 flowData 中的数据
dispatch(updateFlowData({ [currentAppData.parentAppId]: appRes.data })); dispatch(updateFlowData({ [currentAppData.parentAppId]: appRes.data }));
// 同步更新到 canvasDataMap // 同步更新主流程到 canvasDataMap使用父应用ID
if (appRes.data.main?.components) { if (appRes.data.main?.components) {
const { nodes, edges } = convertFlowData(appRes.data.main.components, true); const { nodes: parentNodes, edges: parentEdges } = convertFlowData(appRes.data.main.components, true);
dispatch(updateCanvasDataMap({ dispatch(updateCanvasDataMap({
...canvasDataMap, ...canvasDataMap,
[currentAppData.parentAppId]: { nodes, edges } [currentAppData.parentAppId]: { nodes: parentNodes, edges: parentEdges }
})); }));
} }
// 同步更新子流程到 canvasDataMap使用子流程key
dispatch(updateCanvasDataMap({
...canvasDataMap,
[currentAppData.key]: { nodes, edges }
}));
} }
else { else {
Message.error(res.message); Message.error(res.message);
@ -1294,42 +1314,86 @@ export const useFlowCallbacks = (
// 运行处理函数 // 运行处理函数
const handleRun = useCallback(async (running: boolean) => { const handleRun = useCallback(async (running: boolean) => {
const { currentAppData, socketId, appRuntimeData } = store.getState().ideContainer; const { currentAppData, socketId, appRuntimeData } = store.getState().ideContainer;
const appKey = getCurrentAppKey();
if (running) { if (running) {
// 启动运行 // 子流程运行
const params = { if (currentAppData.key.includes('sub')) {
appId: currentAppData.id, // 启动运行
socketId const params = {
}; appId: currentAppData.parentAppId,
const res: any = await runMainFlow(params); socketId,
if (res.code === 200) { subflowId: currentAppData.key
// 设置运行状态为true };
dispatch(updateIsRunning(true)); const res: any = await runSubFlow(params);
// 重置节点状态 if (res.code === 200) {
dispatch(resetNodeStatus()); // 设置运行状态为true
dispatch(updateIsRunning(true));
// 更新运行ID // 重置节点状态
dispatch(updateRuntimeId(res.data)); dispatch(resetNodeStatus());
// 开始运行时动画 // 更新运行ID
setEdges((eds) => eds.map(edge => ({ dispatch(updateRuntimeId(res.data));
...edge,
data: { // 开始运行时动画
...edge.data, setEdges((eds) => eds.map(edge => ({
isRunning: true, ...edge,
animationProgress: 0 data: {
} ...edge.data,
}))); isRunning: true,
animationProgress: 0
}
})));
}
else {
Message.error(res.message);
}
} }
// 主流程运行
else { else {
Message.error(res.message); // 启动运行
const params = {
appId: currentAppData.id,
socketId
};
const res: any = await runMainFlow(params);
if (res.code === 200) {
// 设置运行状态为true
dispatch(updateIsRunning(true));
// 重置节点状态
dispatch(resetNodeStatus());
// 更新运行ID
dispatch(updateRuntimeId(res.data));
// 开始运行时动画
setEdges((eds) => eds.map(edge => ({
...edge,
data: {
...edge.data,
isRunning: true,
animationProgress: 0
}
})));
}
else {
Message.error(res.message);
}
} }
} }
else { else {
// 设置运行状态为false // 设置运行状态为false
dispatch(updateIsRunning(false)); dispatch(updateIsRunning(false));
await stopApp(appRuntimeData[currentAppData.id].runId); // 使用正确的 appKey 获取 runId
const runId = appKey && appRuntimeData[appKey] ? appRuntimeData[appKey].runId : '';
if (runId) {
await stopApp(runId);
}
// 更新运行ID // 更新运行ID
dispatch(updateRuntimeId('')); dispatch(updateRuntimeId(''));
@ -1344,9 +1408,11 @@ export const useFlowCallbacks = (
}))); })));
// 清空当前应用的运行日志 // 清空当前应用的运行日志
dispatch(clearRuntimeLogs({ appId: currentAppData.id })); if (appKey) {
dispatch(clearRuntimeLogs({ appId: appKey }));
}
} }
}, [initialData?.appId]); }, [getCurrentAppKey]);
return { return {
// Event handlers // Event handlers

@ -12,9 +12,21 @@ export const useFlowEditorState = (initialData?: any) => {
const { canvasDataMap, nodeStatusMap, isRunning, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer); const { canvasDataMap, nodeStatusMap, isRunning, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const dispatch = useDispatch(); const dispatch = useDispatch();
// 辅助函数:获取当前应用/子流程的唯一标识符
const getCurrentAppKey = (currentAppData: any) => {
if (!currentAppData) return null;
// 如果是子流程key包含'sub'使用key作为标识符
if (currentAppData.key && currentAppData.key.includes('sub')) {
return currentAppData.key;
}
// 否则使用id
return currentAppData.id;
};
// 获取当前应用的运行状态 // 获取当前应用的运行状态
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id] const currentAppKey = getCurrentAppKey(currentAppData);
? appRuntimeData[currentAppData.id].isRunning const currentAppIsRunning = currentAppKey && appRuntimeData[currentAppKey]
? appRuntimeData[currentAppKey].isRunning
: false; : false;
// 添加编辑弹窗相关状态 // 添加编辑弹窗相关状态

@ -175,8 +175,21 @@ const FlowEditorMain: React.FC<FlowEditorMainProps> = (props) => {
// 从Redux store中获取当前应用的运行状态 // 从Redux store中获取当前应用的运行状态
const { appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer); const { appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id]
? appRuntimeData[currentAppData.id].isRunning // 辅助函数:获取当前应用/子流程的唯一标识符
const getCurrentAppKey = () => {
if (!currentAppData) return null;
// 如果是子流程key包含'sub'使用key作为标识符
if (currentAppData.key && currentAppData.key.includes('sub')) {
return currentAppData.key;
}
// 否则使用id
return currentAppData.id;
};
const currentAppKey = getCurrentAppKey();
const currentAppIsRunning = currentAppKey && appRuntimeData[currentAppKey]
? appRuntimeData[currentAppKey].isRunning
: false; : false;
// 在应用编排模式下useDefault为false禁用删除功能 // 在应用编排模式下useDefault为false禁用删除功能

@ -29,10 +29,22 @@ const ActionBar: React.FC<ActionBarProps> = ({
}) => { }) => {
const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer); const { logBarStatus, appRuntimeData, currentAppData } = useSelector((state: any) => state.ideContainer);
const dispatch = useDispatch(); const dispatch = useDispatch();
// 辅助函数:获取当前应用/子流程的唯一标识符
const getCurrentAppKey = () => {
if (!currentAppData) return null;
// 如果是子流程key包含'sub'使用key作为标识符
if (currentAppData.key && currentAppData.key.includes('sub')) {
return currentAppData.key;
}
// 否则使用id
return currentAppData.id;
};
// 获取当前应用的运行状态 // 获取当前应用的运行状态
const currentAppIsRunning = currentAppData?.id && appRuntimeData[currentAppData.id] const currentAppKey = getCurrentAppKey();
? appRuntimeData[currentAppData.id].isRunning const currentAppIsRunning = currentAppKey && appRuntimeData[currentAppKey]
? appRuntimeData[currentAppKey].isRunning
: false; : false;
const changeLogBarStatus = () => { const changeLogBarStatus = () => {

@ -21,10 +21,13 @@ export const projectFlowHandle = (initialData, useDefault, setNodes, setEdges, d
setNodes(convertedNodes); setNodes(convertedNodes);
setEdges(initialEdges); setEdges(initialEdges);
if (initialData?.appId) { // 确定要使用的key: 对于子流程使用flowId对于主流程使用appId
const cacheKey = initialData?.flowId || initialData?.appId;
if (cacheKey) {
dispatch(updateCanvasDataMap({ dispatch(updateCanvasDataMap({
...canvasDataMap, ...canvasDataMap,
[initialData.appId]: { nodes: convertedNodes, edges: initialEdges } [cacheKey]: { nodes: convertedNodes, edges: initialEdges }
})); }));
} }
}; };

@ -30,7 +30,7 @@ import ComponentList from '@/pages/componentDevelopment/componentList';
import ComponentCoding from '@/pages/componentDevelopment/componentCoding'; import ComponentCoding from '@/pages/componentDevelopment/componentCoding';
import ComponentDeployment from '@/pages/componentDevelopment/componentDeployment'; import ComponentDeployment from '@/pages/componentDevelopment/componentDeployment';
import ComponentTest from '@/pages/componentDevelopment/componentTest'; import ComponentTest from '@/pages/componentDevelopment/componentTest';
import ComponentEnv from "@/pages/componentDevelopment/componentEnv" import ComponentEnv from '@/pages/componentDevelopment/componentEnv';
import { getUserToken } from '@/api/user'; import { getUserToken } from '@/api/user';
import { Message } from '@arco-design/web-react'; import { Message } from '@arco-design/web-react';
import { queryEventItemBySceneId, queryEventItemBySceneIdOld } from '@/api/event'; import { queryEventItemBySceneId, queryEventItemBySceneIdOld } from '@/api/event';
@ -80,7 +80,6 @@ function IDEContainer() {
// console.log('收到WebSocket消息:', event.data); // console.log('收到WebSocket消息:', event.data);
const socketMessage = JSON.parse(event.data); const socketMessage = JSON.parse(event.data);
if (socketMessage?.socketId) dispatch(updateSocketId(socketMessage.socketId)); if (socketMessage?.socketId) dispatch(updateSocketId(socketMessage.socketId));
// 处理节点状态更新 // 处理节点状态更新
if (socketMessage?.nodeLog) { if (socketMessage?.nodeLog) {
const { nodeId, state, runLog } = socketMessage.nodeLog; const { nodeId, state, runLog } = socketMessage.nodeLog;
@ -122,12 +121,13 @@ function IDEContainer() {
useEffect(() => { useEffect(() => {
const handleOpenSubNodeTab = async (event: CustomEvent) => { const handleOpenSubNodeTab = async (event: CustomEvent) => {
const { node } = event.detail; const { node } = event.detail;
const subCompList = flowData[currentAppData.id].subs; const subCompList = flowData[currentAppData.id]?.subs || [];
const customDef = isJSON(node.data.component.customDef) ? JSON.parse(node.data.component.customDef) : {}; const customDef = isJSON(node.data.component.customDef) ? JSON.parse(node.data.component.customDef) : {};
const currentSubComp = subCompList.find((item) => item.flowId === customDef.subflowId); const currentSubComp = subCompList.find((item) => item.flowId === customDef.subflowId);
// 根据节点信息创建新的标签页 // 根据节点信息创建新的标签页
if (currentSubComp && Object.keys(currentSubComp).length > 0) { if (currentSubComp && Object.keys(currentSubComp).length > 0) {
await getAppList(); // await getAppList(); // 避免重置 subMenuData 导致 sidebar 数据丢失
const newNodeKey = currentSubComp.flowId; const newNodeKey = currentSubComp.flowId;
// 查找菜单项 // 查找菜单项
@ -180,6 +180,9 @@ function IDEContainer() {
}); });
} }
} }
else {
Message.error('未找到对应的复合组件');
}
}; };
// 添加事件监听器 // 添加事件监听器
@ -257,6 +260,7 @@ function IDEContainer() {
useEffect(() => { useEffect(() => {
if (selected.key) { if (selected.key) {
setOpenedTabs(prev => new Set(prev).add(selected.key!)); setOpenedTabs(prev => new Set(prev).add(selected.key!));
handleTabChange(selected.key)
} }
}, [selected.key]); }, [selected.key]);
@ -269,7 +273,6 @@ function IDEContainer() {
const isOpened = openedTabs.has(selected.key); const isOpened = openedTabs.has(selected.key);
// 检查是否是当前选中的路径 // 检查是否是当前选中的路径
const isSelected = selected.path === path; const isSelected = selected.path === path;
// 只有已打开且已经选中的组件才渲染通过CSS控制显示/隐藏 // 只有已打开且已经选中的组件才渲染通过CSS控制显示/隐藏
return isOpened && isSelected ? ( return isOpened && isSelected ? (
<div <div

@ -52,7 +52,7 @@ const LogBar: React.FC<LogBarProps> = () => {
const [activeTab, setActiveTab] = useState('1'); const [activeTab, setActiveTab] = useState('1');
const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用 ResizeBox 容器 const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用 ResizeBox 容器
const [validationLogs, setValidationLogs] = useState<LogMessage[]>([]); const [validationLogs, setValidationLogs] = useState<LogMessage[]>([]);
const [runtimeLogs, setRuntimeLogs] = useState<LogMessage[]>([]); // 添加运行时日志状态 const [runtimeLogs, setruntimeLogs] = useState<LogMessage[]>([]); // 添加运行时日志状态
const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态 const [logContainerHeight, setLogContainerHeight] = useState('250px'); // 添加日志容器高度状态
const [runtimeData, setRuntimeData] = useState<RuntimeData>({}); // 添加运行数据状态 const [runtimeData, setRuntimeData] = useState<RuntimeData>({}); // 添加运行数据状态
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -60,6 +60,17 @@ const LogBar: React.FC<LogBarProps> = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
// 辅助函数:获取当前应用/子流程的唯一标识符
const getCurrentAppKey = () => {
if (!currentAppData) return null;
// 如果是子流程key包含'sub'使用key作为标识符
if (currentAppData.key && currentAppData.key.includes('sub')) {
return currentAppData.key;
}
// 否则使用id
return currentAppData.id;
};
// 处理 Tab 点击事件 // 处理 Tab 点击事件
const handleTabClick = (key: string) => { const handleTabClick = (key: string) => {
// 如果点击当前激活的 tab则切换收起状态 // 如果点击当前激活的 tab则切换收起状态
@ -125,19 +136,19 @@ const LogBar: React.FC<LogBarProps> = () => {
timestamp timestamp
}; };
setRuntimeLogs(prev => [...prev, newLog]); setruntimeLogs(prev => [...prev, newLog]);
// 自动切换到运行日志tab并展开logBar // 自动切换到运行日志tab并展开logBar
dispatch(updateLogBarStatus(true)); dispatch(updateLogBarStatus(true));
// 同时将日志添加到当前应用的运行日志中 // 同时将日志添加到当前应用的运行日志中
const appId = currentAppData?.id; const appKey = getCurrentAppKey();
if (appId) { if (appKey) {
dispatch({ dispatch({
type: 'ideContainer/addRuntimeLog', type: 'ideContainer/addRuntimeLog',
payload: { payload: {
log: newLog, log: newLog,
appId: appId appId: appKey // 使用 appKey 而不是 id
} }
}); });
} }
@ -151,21 +162,22 @@ const LogBar: React.FC<LogBarProps> = () => {
return () => { return () => {
document.removeEventListener('logMessage', handleLogMessage as EventListener); document.removeEventListener('logMessage', handleLogMessage as EventListener);
}; };
}, [dispatch, currentAppData?.id]); }, [dispatch, currentAppData]);
// 实现轮询获取运行数据 // 实现轮询获取运行数据
useEffect(() => { useEffect(() => {
let intervalId: NodeJS.Timeout | null = null; let intervalId: NodeJS.Timeout | null = null;
const currentAppKey = getCurrentAppKey();
// 只有在当前tab是运行数据且有当前应用时才开始轮询 // 只有在当前tab是运行数据且有当前应用时才开始轮询
if (activeTab === '3' && currentAppData?.id && logBarStatus && appRuntimeData[currentAppData.id]?.runId) { if (activeTab === '3' && currentAppKey && logBarStatus && appRuntimeData[currentAppKey]?.runId) {
const fetchRuntimeData = async () => { const fetchRuntimeData = async () => {
try { try {
setLoading(true); setLoading(true);
const response = await getNodeData(appRuntimeData[currentAppData.id].runId); const response = await getNodeData(appRuntimeData[currentAppKey].runId);
setRuntimeData(prev => ({ setRuntimeData(prev => ({
...prev, ...prev,
[currentAppData.id]: response.data [currentAppKey]: response.data
})); }));
} catch (error) { } catch (error) {
console.error('获取运行数据失败:', error); console.error('获取运行数据失败:', error);
@ -187,7 +199,7 @@ const LogBar: React.FC<LogBarProps> = () => {
clearInterval(intervalId); clearInterval(intervalId);
} }
}; };
}, [activeTab, currentAppData?.id, logBarStatus]); }, [activeTab, currentAppData, logBarStatus]);
// 渲染校验日志内容 // 渲染校验日志内容
const renderValidationLogs = () => { const renderValidationLogs = () => {
@ -214,8 +226,9 @@ const LogBar: React.FC<LogBarProps> = () => {
// 渲染运行时日志内容 // 渲染运行时日志内容
const renderRuntimeLogs = () => { const renderRuntimeLogs = () => {
// 获取当前应用的运行日志 // 获取当前应用的运行日志
const currentAppLogs = currentAppData?.id && appRuntimeData[currentAppData.id] const currentAppKey = getCurrentAppKey();
? appRuntimeData[currentAppData.id].logs || [] const currentAppLogs = currentAppKey && appRuntimeData[currentAppKey]
? appRuntimeData[currentAppKey].logs || []
: []; : [];
return ( return (
@ -240,7 +253,8 @@ const LogBar: React.FC<LogBarProps> = () => {
// 渲染运行数据内容 // 渲染运行数据内容
const renderRuntimeData = () => { const renderRuntimeData = () => {
const currentAppDataContent = currentAppData?.id ? runtimeData[currentAppData.id] : null; const currentAppKey = getCurrentAppKey();
const currentAppDataContent = currentAppKey ? runtimeData[currentAppKey] : null;
return ( return (
<div style={{ padding: '10px', height: 'calc(100% - 40px)', overflowY: 'auto' }}> <div style={{ padding: '10px', height: 'calc(100% - 40px)', overflowY: 'auto' }}>

@ -93,10 +93,11 @@ const NavBar: React.ForwardRefRenderFunction<NavBarRef, NavBarProps> = ({
const currentIndex = tabs.findIndex(tab => tab.key === key); const currentIndex = tabs.findIndex(tab => tab.key === key);
const nextIndex = currentIndex > 0 ? currentIndex - 1 : 0; const nextIndex = currentIndex > 0 ? currentIndex - 1 : 0;
const nextTab = newTabs[nextIndex]; const nextTab = newTabs[nextIndex];
setActiveTab(nextTab.key); setActiveTab(nextTab.key);
onTabChange?.(nextTab.key); onTabChange?.(nextTab.key);
} else { }
else {
// 如果没有更多tabs重置状态 // 如果没有更多tabs重置状态
setActiveTab(''); setActiveTab('');
onTabChange?.(''); onTabChange?.('');

@ -208,6 +208,8 @@ const SideBar: React.FC<SideBarProps> = ({
}); // 添加右键菜单状态 }); // 添加右键菜单状态
// 用于存储隐藏的节点ID // 用于存储隐藏的节点ID
const [hiddenNodes, setHiddenNodes] = useState<Set<string>>(new Set()); const [hiddenNodes, setHiddenNodes] = useState<Set<string>>(new Set());
// 用于控制展开的节点
const [expandedKeys, setExpandedKeys] = useState<string[]>(['0']);
const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器 const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器
const contextMenuRef = useRef<HTMLDivElement>(null); // 右键菜单引用 const contextMenuRef = useRef<HTMLDivElement>(null); // 右键菜单引用
const { menuData, info, canvasDataMap } = useSelector(state => state.ideContainer); const { menuData, info, canvasDataMap } = useSelector(state => state.ideContainer);
@ -396,29 +398,40 @@ const SideBar: React.FC<SideBarProps> = ({
}; };
}); });
children.children[1].children = Object.keys(res.data.compList).map(item => { children.children[1].children = Object.keys(res.data.compList).map(item => {
return { // 对于普通组件,直接渲染组件名称列表
title: compTypeMap[item], if (item === 'appComponent') {
icon: item === 'appComponent' ? '/ideContainer/icon/app1.png' : '/ideContainer/icon/complexApp.png', return {
children: item === 'appComponent' ? res.data.compList[item].map(title => { title: compTypeMap[item],
return { icon: '/ideContainer/icon/app1.png',
title: title, children: res.data.compList[item].map(title => {
children: null, return {
icon: '/ideContainer/icon/tool.png' title: title,
}; children: null,
}) : res.data.subs.map(info => { icon: '/ideContainer/icon/tool.png'
return { };
title: info.flowName, })
children: null, };
icon: '/ideContainer/icon/tool.png', }
compData: info, // 对于复合组件,直接渲染复合组件本身(不渲染子流程)
path: 'complexFlow', else {
key: info.flowId, return {
pathTitle: `${data.title} / ${info.flowName}`, title: compTypeMap[item],
parentKey: 'appList', icon: '/ideContainer/icon/complexApp.png',
parentAppId: data.id children: res.data.subs.map(info => {
}; return {
}) title: info.flowName,
}; children: null,
icon: '/ideContainer/icon/tool.png',
compData: info,
path: 'complexFlow',
key: info.flowId,
pathTitle: `${data.title} / ${info.flowName}`,
parentKey: 'appList',
parentAppId: data.id
};
})
};
}
}); });
const findMenuItem = (menuItems: any[], key: string): any => { const findMenuItem = (menuItems: any[], key: string): any => {
@ -588,11 +601,11 @@ const SideBar: React.FC<SideBarProps> = ({
// 监听导航到Tab的事件 // 监听导航到Tab的事件
const handleNavigateToTab = (event: CustomEvent) => { const handleNavigateToTab = (event: CustomEvent) => {
const { path } = event.detail; const { path } = event.detail;
// 查找对应的菜单项 // 查找对应的菜单项
const menuItems = menuData[identity]; const menuItems = menuData[identity];
if (!menuItems) return; if (!menuItems) return;
const findMenuItem = (items: any[]): any => { const findMenuItem = (items: any[]): any => {
for (const item of items) { for (const item of items) {
if (item.path === path) { if (item.path === path) {
@ -605,7 +618,7 @@ const SideBar: React.FC<SideBarProps> = ({
} }
return null; return null;
}; };
const menuItem = findMenuItem(menuItems); const menuItem = findMenuItem(menuItems);
if (menuItem) { if (menuItem) {
// 触发菜单选择 // 触发菜单选择
@ -792,7 +805,8 @@ const SideBar: React.FC<SideBarProps> = ({
{/* 子菜单 */} {/* 子菜单 */}
<div onContextMenu={handleContextMenu}> <div onContextMenu={handleContextMenu}>
<Tree <Tree
defaultExpandedKeys={['0']} // 整个属性去掉就会展开全部 expandedKeys={expandedKeys}
onExpand={(keys) => setExpandedKeys(keys as string[])}
selectedKeys={[]} // 移除选中样式 selectedKeys={[]} // 移除选中样式
onMouseDown={handleMouseDown} onMouseDown={handleMouseDown}
onSelect={async (_selectedKeys, info) => { onSelect={async (_selectedKeys, info) => {

@ -59,6 +59,18 @@ const initialState: IDEContainerState = {
} }
}; };
// 辅助函数:获取当前应用/子流程的唯一标识符
// 对于子流程,使用 key如 sub_xxx对于主流程使用 id
const getCurrentAppKey = (currentAppData: any) => {
if (!currentAppData) return null;
// 如果是子流程key包含'sub'使用key作为标识符
if (currentAppData.key && currentAppData.key.includes('sub')) {
return currentAppData.key;
}
// 否则使用id
return currentAppData.id;
};
// 创建切片 // 创建切片
const ideContainerSlice = createSlice({ const ideContainerSlice = createSlice({
name: 'ideContainer', name: 'ideContainer',
@ -113,10 +125,10 @@ const ideContainerSlice = createSlice({
} }
// 同时更新当前应用的节点状态 // 同时更新当前应用的节点状态
const appId = state.currentAppData?.id; const appKey = getCurrentAppKey(state.currentAppData);
if (appId) { if (appKey) {
if (!state.appRuntimeData[appId]) { if (!state.appRuntimeData[appKey]) {
state.appRuntimeData[appId] = { state.appRuntimeData[appKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
logs: [], logs: [],
@ -125,7 +137,7 @@ const ideContainerSlice = createSlice({
eventlisteneList: [] eventlisteneList: []
}; };
} }
state.appRuntimeData[appId].nodeStatusMap[nodeId] = status; state.appRuntimeData[appKey].nodeStatusMap[nodeId] = status;
} }
}, },
// 重置节点状态 // 重置节点状态
@ -133,9 +145,9 @@ const ideContainerSlice = createSlice({
state.nodeStatusMap = {}; state.nodeStatusMap = {};
// 同时重置当前应用的节点状态 // 同时重置当前应用的节点状态
const appId = state.currentAppData?.id; const appKey = getCurrentAppKey(state.currentAppData);
if (appId && state.appRuntimeData[appId]) { if (appKey && state.appRuntimeData[appKey]) {
state.appRuntimeData[appId].nodeStatusMap = {}; state.appRuntimeData[appKey].nodeStatusMap = {};
} }
}, },
// 更新运行状态 // 更新运行状态
@ -143,10 +155,10 @@ const ideContainerSlice = createSlice({
state.isRunning = payload; state.isRunning = payload;
// 同时更新当前应用的运行状态 // 同时更新当前应用的运行状态
const appId = state.currentAppData?.id; const appKey = getCurrentAppKey(state.currentAppData);
if (appId) { if (appKey) {
if (!state.appRuntimeData[appId]) { if (!state.appRuntimeData[appKey]) {
state.appRuntimeData[appId] = { state.appRuntimeData[appKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
logs: [], logs: [],
@ -155,14 +167,14 @@ const ideContainerSlice = createSlice({
eventlisteneList: [] eventlisteneList: []
}; };
} }
state.appRuntimeData[appId].isRunning = payload; state.appRuntimeData[appKey].isRunning = payload;
} }
}, },
// 添加运行id // 添加运行id
updateRuntimeId: (state, { payload }) => { updateRuntimeId: (state, { payload }) => {
const appId = state.currentAppData?.id; const appKey = getCurrentAppKey(state.currentAppData);
if (!state.appRuntimeData[appId]) { if (!state.appRuntimeData[appKey]) {
state.appRuntimeData[appId] = { state.appRuntimeData[appKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
logs: [], logs: [],
@ -171,13 +183,13 @@ const ideContainerSlice = createSlice({
eventlisteneList: [] eventlisteneList: []
}; };
} }
state.appRuntimeData[appId].runId = payload; state.appRuntimeData[appKey].runId = payload;
}, },
// 更新事件节点列表 // 更新事件节点列表
updateEventNodeList: (state, { payload }) => { updateEventNodeList: (state, { payload }) => {
const appId = state.currentAppData?.id; const appKey = getCurrentAppKey(state.currentAppData);
if (!state.appRuntimeData[appId]) { if (!state.appRuntimeData[appKey]) {
state.appRuntimeData[appId] = { state.appRuntimeData[appKey] = {
nodeStatusMap: {}, nodeStatusMap: {},
isRunning: false, isRunning: false,
logs: [], logs: [],
@ -186,7 +198,7 @@ const ideContainerSlice = createSlice({
eventlisteneList: [] eventlisteneList: []
}; };
} }
state.appRuntimeData[appId] = { ...state.appRuntimeData[appId], ...payload }; state.appRuntimeData[appKey] = { ...state.appRuntimeData[appKey], ...payload };
}, },
// 添加运行日志 // 添加运行日志
addRuntimeLog: (state, { payload }) => { addRuntimeLog: (state, { payload }) => {

Loading…
Cancel
Save