import React, { useState, useEffect, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { updateSocketId, updateNodeStatus, updateEventListOld } from '@/store/ideContainer';
import useWebSocket from '@/hooks/useWebSocket';
import { isJSON, getUrlParams } from '@/utils/common';
import styles from './style/index.module.less';
import SideBar from './sideBar';
import LogBar from './logBar';
import RightSideBar from './rightSideBar';
import NavBar, { NavBarRef } from './navBar';
import { Selected } from '@/pages/ideContainer/types';
import {
updateCurrentAppData,
updateInfo,
updateProjectComponentData,
updateEventList,
updateGlobalVarList
} from '@/store/ideContainer';
import { getAppListBySceneId } from '@/api/apps';
import { getProjectComp } from '@/api/scene';
import ProjectContainer from '@/pages/orchestration/project';
import ComplexContainer from '@/pages/orchestration/complex';
import ApplicationContainer from '@/pages/orchestration/application';
import EventContainer from '@/pages/orchestration/event';
import GlobalVarContainer from '@/pages/orchestration/globalVar';
import AppCompComponent from '@/pages/orchestration/appCompComponent';
import ComponentList from '@/pages/componentDevelopment/componentList';
import ComponentCoding from '@/pages/componentDevelopment/componentCoding';
import ComponentDeployment from '@/pages/componentDevelopment/componentDeployment';
import ComponentTest from '@/pages/componentDevelopment/componentTest';
import ComponentEnv from '@/pages/componentDevelopment/componentEnv';
import SystemResource from '@/pages/componentDevelopment/systemResource';
import { getUserToken } from '@/api/user';
import { Message } from '@arco-design/web-react';
import { queryEventItemBySceneId, queryEventItemBySceneIdOld } from '@/api/event';
import { getGlobalList } from '@/api/global';
type UrlParamsOptions = {
identity?: string;
[key: string]: string
};
const CompListComponent = () =>
组件列表
;
const AppInstanceComponent = () => 应用实例
;
const MyComponents = () => 我的组件
;
const MatchingComponents = () => 协同组件
;
const ComponentReview = () => 组件审核
;
// 所有可显示的组件路径列表
const ALL_PATHS = [
'compFlow', 'complexFlow', 'appFlow', 'compList', 'appInstance', 'event', 'globalVar', 'appCompList',
'myComponents', 'matchingComponents', 'componentReview', 'componentList',
'componentCoding', 'componentDeployment', 'componentTest', 'envConfig',
'systemResource'
];
function IDEContainer() {
const [selected, setSelected] = useState({});
const [urlParams, setUrlParams] = useState({});
const [subMenuWidth, setSubMenuWidth] = useState(200); // 子菜单宽度状态
// 用于跟踪已打开的tab,保持组件状态
const [openedTabs, setOpenedTabs] = useState>(new Set());
const [subMenuData, setSubMenuData] = useState({});
const { menuData, flowData, currentAppData } = useSelector((state) => state.ideContainer);
const dispatch = useDispatch();
const navBarRef = useRef(null);
// 初始化WebSocket hook
const ws = useWebSocket({
onOpen: () => {
console.log('WebSocket连接已建立');
},
onClose: () => {
console.log('WebSocket连接已关闭');
},
onError: (event) => {
console.error('WebSocket错误:', event);
},
onMessage: (event) => {
// console.log('收到WebSocket消息:', event.data);
const socketMessage = JSON.parse(event.data);
if (socketMessage?.socketId) dispatch(updateSocketId(socketMessage.socketId));
// 处理节点状态更新
if (socketMessage?.nodeLog) {
const { nodeId, state, runLog, appId } = socketMessage.nodeLog;
// 将状态映射为前端使用的状态
let status = 'waiting';
switch (state) {
case 0: // 运行中
status = 'running';
break;
case 1: // 运行成功
status = 'success';
break;
case -1: // 运行失败
status = 'failed';
break;
default:// 等待运行
status = 'waiting';
break;
}
// 更新节点状态,使用特殊的actionType标记这是运行时状态更新
// 如果后端提供了 appId,则传递给 action 以确保更新正确的应用状态
dispatch(updateNodeStatus({ nodeId, status, appId, actionType: 'RUNTIME_UPDATE' }));
// 只有当存在runLog时才发送日志到logBar
if (runLog) {
const logEvent = new CustomEvent('logMessage', {
detail: {
type: 'runtime',
message: runLog,
timestamp: new Date().toISOString(),
appId // 传递 appId 以便日志正确关联到对应的应用
}
});
document.dispatchEvent(logEvent);
}
}
}
});
// 监听自定义事件,处理打开子节点标签页的逻辑
useEffect(() => {
const handleOpenSubNodeTab = async (event: CustomEvent) => {
const { node } = event.detail;
const subCompList = flowData[currentAppData.id]?.subs || [];
const customDef = isJSON(node.data.component.customDef) ? JSON.parse(node.data.component.customDef) : {};
const currentSubComp = subCompList.find((item) => item.flowId === customDef.subflowId);
// 根据节点信息创建新的标签页
if (currentSubComp && Object.keys(currentSubComp).length > 0) {
// await getAppList(); // 避免重置 subMenuData 导致 sidebar 数据丢失
const newNodeKey = currentSubComp.flowId;
// 查找菜单项
const findMenuItem = (menuItems: any[]): any => {
for (const item of menuItems) {
if (item.key === newNodeKey) {
return item;
}
if (item.children) {
const found = findMenuItem(item.children);
if (found) return found;
}
}
return null;
};
const menuItems = menuData[urlParams.identity];
const menuItem = findMenuItem(menuItems);
if (menuItem) {
// 更新当前应用数据
dispatch(updateCurrentAppData({ ...menuItem }));
// 设置选中项,触发tab创建
setSelected({
...menuItem,
key: menuItem.key,
parentKey: menuItem.parentKey
});
}
else {
// 如果在菜单中找不到对应项,创建一个临时的菜单项
const tempMenuItem = {
key: newNodeKey,
title: currentSubComp.flowName,
pathTitle: `${currentAppData.name} / ${currentSubComp.flowName}`,
path: 'complexFlow',
parentKey: 'appList',
parentAppId: currentAppData.id,
compData: currentSubComp,
children: null
};
dispatch(updateCurrentAppData({ ...tempMenuItem }));
setSelected({
...tempMenuItem,
key: tempMenuItem.key,
parentKey: tempMenuItem.parentKey
});
}
}
else {
Message.error('未找到对应的子流程');
}
};
// 添加事件监听器
document.addEventListener('openSubNodeTab', handleOpenSubNodeTab as EventListener);
// 清理事件监听器
return () => {
document.removeEventListener('openSubNodeTab', handleOpenSubNodeTab as EventListener);
};
}, [menuData, flowData, currentAppData]);
const connectWS = async () => {
const res = await getUserToken();
const token = res.data;
const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
let wsApi = `${protocol}://${window.location.host}/ws/v1/bpms-runtime`;
if (window.location.host.includes('localhost')) {
wsApi = `${process.env.NEXT_PUBLIC_DEV_SOCKET_HOST}/ws/v1/bpms-runtime`;
}
const uri = `${wsApi}?x-auth0-token=${token}`;
ws.connect(uri);
};
const getAppList = async () => {
const res: any = await getAppListBySceneId({
pageSize: 999,
currPage: 1,
sceneId: urlParams.id
});
if (res.code === 200) {
setSubMenuData({ ...subMenuData, 'appList': res.data.list.reverse(), 'appFlow': res.data.list.reverse() });
}
};
const getProjectCompData = async () => {
const res: any = await getProjectComp(urlParams.id);
if (res.code === 200) {
await dispatch(updateProjectComponentData({ [urlParams.id]: res.data }));
}
};
const getEvent = async () => {
const res: any = await queryEventItemBySceneId({ sceneId: urlParams.id });
const res1: any = await queryEventItemBySceneIdOld(urlParams.id);
if (res.code === 200) dispatch(updateEventList(res.data));
if (res1.code === 200) dispatch(updateEventListOld(res1.data));
};
const getGlobalVar = async () => {
const res: any = await getGlobalList(urlParams.id);
if (res.code === 200) dispatch(updateGlobalVarList(res.data));
};
useEffect(() => {
setUrlParams(getUrlParams(window.location.href) as UrlParamsOptions);
dispatch(updateInfo(getUrlParams(window.location.href) as UrlParamsOptions));
}, []);
useEffect(() => {
if (urlParams.id) {
getProjectCompData();
getAppList();
}
}, [urlParams.id]);
useEffect(() => {
if (urlParams.identity && urlParams.identity === 'scene') {
connectWS();
getEvent();
getGlobalVar();
}
}, [urlParams.identity]);
// 监听刷新应用列表事件(特殊停止后触发)
useEffect(() => {
const handleRefreshAppList = () => {
if (urlParams.id) {
getAppList();
}
};
document.addEventListener('refreshAppList', handleRefreshAppList);
return () => {
document.removeEventListener('refreshAppList', handleRefreshAppList);
};
}, [urlParams.id]);
// 页面加载后默认选择第一个菜单项
useEffect(() => {
if (urlParams.identity && menuData[urlParams.identity]?.length > 0 && !selected.key) {
const firstMenuItem = menuData[urlParams.identity][0];
if (firstMenuItem) {
setSelected({
...firstMenuItem,
key: firstMenuItem.key,
parentKey: firstMenuItem.parentKey || firstMenuItem.key
});
dispatch(updateCurrentAppData({ ...firstMenuItem }));
}
}
}, [urlParams.identity, menuData]);
// 当selected.path变化时,添加到已打开的tab集合中
useEffect(() => {
if (selected.key) {
setOpenedTabs(prev => new Set(prev).add(selected.key!));
// 更新当前应用数据,确保画布能正确显示
dispatch(updateCurrentAppData({ ...selected }));
}
}, [selected.key]);
// 根据选中的菜单项渲染对应组件
const renderContent = () => {
return (
{ALL_PATHS.map((path, i) => {
// 检查该路径的组件是否已打开
const isOpened = openedTabs.has(selected.key);
// 检查是否是当前选中的路径
const isSelected = selected.path === path;
// 只有已打开且已经选中的组件才渲染,通过CSS控制显示/隐藏
return isOpened && isSelected ? (
{getContentByPath(path)}
) : null;
})}
{/* 默认内容,当没有选中任何tab时显示 */}
{!selected.path && (
请选择应用,预览配置信息
)}
);
};
// 根据路径获取对应的组件
const getContentByPath = (path: string) => {
switch (path) {
case 'compFlow':
return ;
case 'complexFlow':
return ;
case 'appFlow':
return ;
case 'compList':
return ;
case 'appInstance':
return ;
case 'event':
return ;
case 'globalVar':
return ;
case 'appCompList':
return ;
case 'myComponents':
return ;
case 'matchingComponents':
return ;
case 'componentList':
return ;
case 'componentReview':
return ;
case 'componentCoding':
return ;
case 'componentDeployment':
return ;
case 'componentTest':
return ;
case 'envConfig':
return ;
case 'systemResource':
return ;
default:
return 未知页面
;
}
};
// 处理子菜单区域的拖拽调整大小
const handleSubMenuResize = (e: MouseEvent, { width }: { width: number }) => {
setSubMenuWidth(width);
};
// 处理tab切换
const handleTabChange = (key: string) => {
if (key) {
const findMenuItem = (menuItems: any[], key: string): any => {
for (const item of menuItems) {
if (item.key === key) {
return item;
}
if (item.children) {
const found = findMenuItem(item.children, key);
if (found) return found;
}
}
return null;
};
const menuItems = menuData[urlParams.identity];
const menuItem = findMenuItem(menuItems, key);
dispatch(updateCurrentAppData({ ...menuItem }));
if (menuItem) {
setSelected({
...menuItem,
key: menuItem.key,
parentKey: menuItem.parentKey
});
// 将选中的tab添加到已打开的tabs集合中
setOpenedTabs(prev => new Set(prev).add(menuItem.key));
}
}
else {
// 没有激活的tab,重置selected状态
setSelected({});
}
};
// 处理tab关闭
const handleTabClose = (key: string) => {
// 从已打开的tab集合中移除
const newOpenedTabs = new Set(openedTabs);
newOpenedTabs.delete(key);
setOpenedTabs(newOpenedTabs);
// 如果关闭的是当前激活的tab,则重置selected状态
// if (path === selected.path) {
// setSelected({});
// }
};
return (
<>
setSelected(select)}
onRefresh={() => getAppList()}
onDeleteApp={(appId) => {
// 从已打开的tab集合中移除被删除的应用
const newOpenedTabs = new Set(openedTabs);
// 查找并删除与该应用相关的tab
const tabsToDelete = Array.from(newOpenedTabs).filter(key => key?.includes(appId));
tabsToDelete.forEach(tabKey => newOpenedTabs.delete(tabKey));
setOpenedTabs(newOpenedTabs);
// 如果当前选中的tab与被删除的应用相关,则重置selected状态
if (selected.key && selected.key.includes(appId)) {
setSelected({});
}
// 通知NavBar删除对应的tab
navBarRef.current?.deleteTabByKey(`compFlow-${appId}`);
}}
/>
{/*顶部导航栏*/}
{/*页面渲染*/}
{renderContent()}
{/*底部日志栏*/}
{urlParams.identity !== 'componentDevelopment' && ['appList', 'appFlow'].includes(selected.parentKey) &&
}
{['appList', 'appFlow'].includes(selected.parentKey) &&
getProjectCompData()}>}
>
);
}
export default IDEContainer;