You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
9.5 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useEffect, useState, useRef } from 'react';
import { 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 { updateInfo, updateProjectComponentData } from '@/store/ideContainer';
import { useDispatch, useSelector } from 'react-redux';
import { getAppListBySceneId } from '@/api/apps';
import { getProjectComp } from '@/api/scene';
import ProjectContainer from '@/pages/orchestration/project';
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';
type UrlParamsOptions = {
identity?: string;
[key: string]: string
};
const CompListComponent = () => <div style={{ height: '100%', width: '100%' }}></div>;
const AppInstanceComponent = () => <div style={{ height: '100%', width: '100%' }}></div>;
const MyComponents = () => <div style={{ height: '100%', width: '100%' }}></div>;
const MatchingComponents = () => <div style={{ height: '100%', width: '100%' }}></div>;
const ComponentReview = () => <div style={{ height: '100%', width: '100%' }}></div>;
const EnvConfig = () => <div style={{ height: '100%', width: '100%' }}></div>;
// 所有可显示的组件路径列表
const ALL_PATHS = [
'compFlow', 'appFlow', 'compList', 'appInstance', 'event', 'globalVar', 'appCompList',
'myComponents', 'matchingComponents', 'componentReview', 'componentList',
'componentCoding', 'componentDeployment', 'componentTest', 'envConfig'
];
function IDEContainer() {
const [selected, setSelected] = useState<Selected>({});
const [urlParams, setUrlParams] = useState<UrlParamsOptions>({});
const [subMenuWidth, setSubMenuWidth] = useState(200); // 子菜单宽度状态
// 用于跟踪已打开的tab保持组件状态
const [openedTabs, setOpenedTabs] = useState<Set<string>>(new Set());
const [subMenuData, setSubMenuData] = useState<any>({});
const { menuData } = useSelector((state) => state.ideContainer);
const dispatch = useDispatch();
const navBarRef = useRef<NavBarRef>(null);
const getAppList = async () => {
const res: any = await getAppListBySceneId({
pageSize: 999,
currPage: 1,
sceneId: urlParams.id
});
if (res.code === 200) setSubMenuData({ 'appList': 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 }));
}
};
useEffect(() => {
setUrlParams(getUrlParams(window.location.href) as UrlParamsOptions);
dispatch(updateInfo(getUrlParams(window.location.href) as UrlParamsOptions));
}, []);
useEffect(() => {
if (urlParams.id) {
getProjectCompData();
getAppList();
}
}, [urlParams.id]);
// 当selected.path变化时添加到已打开的tab集合中
useEffect(() => {
if (selected.key) {
setOpenedTabs(prev => new Set(prev).add(selected.key!));
}
}, [selected.key]);
// 根据选中的菜单项渲染对应组件
const renderContent = () => {
return (
<div style={{ height: '100%', width: '100%', position: 'relative', boxSizing: 'border-box' }}>
{ALL_PATHS.map((path, i) => {
// 检查该路径的组件是否已打开
const isOpened = openedTabs.has(selected.key);
// 检查是否是当前选中的路径
const isSelected = selected.path === path;
// 只有已打开且已经选中的组件才渲染通过CSS控制显示/隐藏
return isOpened && isSelected ? (
<div
key={`${selected.key}-${i}`}
style={{
display: isSelected ? 'block' : 'none',
height: '100%'
}}
>
{getContentByPath(path)}
</div>
) : null;
})}
{/* 默认内容当没有选中任何tab时显示 */}
{!selected.path && (
<div style={{
width: '100%',
height: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
backgroundColor: '#ffffff'
}}>
<img
src={'/ideContainer/background/empty.png'}
/>
<span style={{
fontSize: '20px',
color: 'rgba(141, 141, 153, 1)'
}}>
</span>
</div>
)}
</div>
);
};
// 根据路径获取对应的组件
const getContentByPath = (path: string) => {
switch (path) {
case 'compFlow':
return <ProjectContainer selected={selected} />;
case 'appFlow':
return <ApplicationContainer />;
case 'compList':
return <CompListComponent />;
case 'appInstance':
return <AppInstanceComponent />;
case 'event':
return <EventContainer />;
case 'globalVar':
return <GlobalVarContainer />;
case 'appCompList':
return <AppCompComponent />;
case 'myComponents':
return <MyComponents />;
case 'matchingComponents':
return <MatchingComponents />;
case 'componentList':
return <ComponentList />;
case 'componentReview':
return <ComponentReview />;
case 'componentCoding':
return <ComponentCoding />;
case 'componentDeployment':
return <ComponentDeployment />;
case 'componentTest':
return <ComponentTest />;
case 'envConfig':
return <EnvConfig />;
default:
return <div></div>;
}
};
// 处理子菜单区域的拖拽调整大小
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);
if (menuItem) {
setSelected({
...menuItem,
key: menuItem.key,
parentKey: menuItem.parentKey
});
}
}
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 (
<>
<div className={styles.IDEContainer}>
<SideBar
selected={selected}
identity={urlParams.identity}
subMenuData={subMenuData}
showSubMenu={['appList', 'appFlow'].includes(selected.parentKey)}
onMenuSelect={(select) => 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}`);
}}
/>
<div className={styles.content}>
<div className={styles.mainContent}>
{/*顶部导航栏*/}
<NavBar
ref={navBarRef}
selected={selected}
menuData={menuData[urlParams.identity]}
onTabChange={handleTabChange}
onTabClose={handleTabClose}
></NavBar>
{/*页面渲染*/}
{renderContent()}
{/*底部日志栏*/}
{urlParams.identity !== 'componentDevelopment' && ['appList', 'appFlow'].includes(selected.parentKey) &&
<LogBar></LogBar>}
</div>
</div>
{['appList', 'appFlow'].includes(selected.parentKey) &&
<RightSideBar updateProjectComp={() => getProjectCompData()}></RightSideBar>}
</div>
</>
);
}
export default IDEContainer;