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;