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.

963 lines
30 KiB
TypeScript

import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react';
import styles from './style/sideBar.module.less';
import {
ResizeBox,
Tree,
Input,
Button,
Modal,
Form,
Message,
Dropdown,
Menu,
Popconfirm
} from '@arco-design/web-react';
import {
IconApps,
IconMore,
IconDelete,
IconEdit,
IconEye,
IconSearch,
IconPlus,
IconEyeInvisible,
IconExpand,
IconShrink,
IconLeft,
IconLoading
} from '@arco-design/web-react/icon';
import { menuData1, menuData2 } from './config/menuData';
import { Selected } from '@/pages/ideContainer/types';
import { useDispatch, useSelector } from 'react-redux';
import {
updateMenuData,
updateFlowData,
updateCanvasDataMap,
updateCurrentAppData,
updateEventListOld,
updateEventNodeList,
updateIsRunning,
updateRuntimeId
} from '@/store/ideContainer';
import { addApp, getProjectEnv, editApp, deleteApp } from '@/api/apps';
import _ from 'lodash';
import { getAppInfoNew } from '@/api/appRes';
import { getAppEventData } from '@/api/appEvent';
import { queryEventItemBySceneIdOld } from '@/api/event';
import { convertFlowData } from '@/utils/convertFlowData';
const TreeNode = Tree.Node;
const FormItem = Form.Item;
const TextArea = Input.TextArea;
const MenuItem = Menu.Item;
interface MenuItemType {
title: string;
key?: string;
children?: MenuItemType[];
path?: string;
icon?: React.ReactNode;
[key: string]: string | MenuItemType[] | React.ReactNode;
}
interface SideBarProps {
subMenuData: any;
selected?: any;
identity?: string;
showSubMenu: boolean;
onMenuSelect?: (selected: Selected) => void;
onRefresh: () => void;
onDeleteApp?: (appId: string) => void; // 添加删除应用的回调函数
}
const compTypeMap = {
appComponent: '普通组件',
subComponent: '子流程'
};
const AppHandleModal = ({ appInfo, visible, type, onChangeVisible, onClose, onRefresh }) => {
const { info } = useSelector(state => state.ideContainer);
const [form] = Form.useForm();
useEffect(() => {
if (type === 'EDIT' && appInfo && visible) {
form.setFieldsValue({
id: appInfo?.id,
name: appInfo?.name,
description: appInfo?.description,
published: appInfo?.published === 1,
logo: 'scene04.png', // 先写死
sceneId: info.id
});
}
}, [type, appInfo, visible, form, info.id]);
const onOk = async () => {
await form.validate();
const formData = form.getFields();
// 新增
if (type === 'ADD') {
// 新增应用的入参
const params = {
description: formData.description,
name: formData.name,
logo: 'scene04.png', // 先写死
published: 0,
sceneId: info.id
};
const res: any = await addApp(params);
if (res.code === 200) Message.success('创建成功');
else Message.error(res.message);
}
// 编辑
else {
const res: any = await editApp(formData);
if (res.code === 200) Message.success('编辑成功');
else Message.error(res.message);
}
// 清空表单数据和其他变量数据
form.resetFields();
onRefresh();
onClose();
};
return (
<Modal
title={type === 'ADD' ? '新增应用' : '编辑应用'}
visible={visible}
onOk={() => onOk()}
onCancel={() => onChangeVisible(false)}
autoFocus={false}
focusLock={true}
maskClosable={false}
>
<Form form={form} autoComplete="off">
<FormItem
field="name"
label="应用名称"
required
rules={[
{
validator(value, cb) {
if (!value) {
return cb('请填写应用名称');
}
// 检查是否只包含汉字
const chineseOnlyRegex = /^[\u4e00-\u9fa5]+$/;
if (!chineseOnlyRegex.test(value)) {
return cb('应用名称只能输入汉字');
}
return cb();
}
}
]}
>
<Input placeholder="请输入应用名称" />
</FormItem>
<FormItem
field="description"
label="应用描述"
required
rules={[
{
validator(value, cb) {
if (!value) {
return cb('请填写应用描述');
}
return cb();
}
}
]}
>
{/*<Input placeholder="请输入工程描述" />*/}
<TextArea placeholder="请输入应用描述" />
</FormItem>
</Form>
</Modal>
);
};
const SideBar: React.FC<SideBarProps> = ({
selected,
identity,
subMenuData,
showSubMenu,
onMenuSelect,
onRefresh,
onDeleteApp
}) => {
const [menu, setMenu] = useState<MenuItemType[]>([]);
const [searchValue, setSearchValue] = useState(''); // 添加搜索状态
const [subMenuWidth, setSubMenuWidth] = useState(300); // 子菜单宽度状态
const [isSubMenuCollapsed, setIsSubMenuCollapsed] = useState(false); // 子菜单收起状态
const [activeKey, setActiveKey] = useState(0);
const [showModal, setShowModal] = useState(false);
const [modalType, setModalType] = useState<'ADD' | 'EDIT'>('ADD');
const [currentApp, setCurrentApp] = useState<any>(null);
const [contextMenu, setContextMenu] = useState<{
visible: boolean;
x: number;
y: number;
nodeData: any;
}>({
visible: false,
x: 0,
y: 0,
nodeData: null
}); // 添加右键菜单状态
// 用于存储隐藏的节点ID
const [hiddenNodes, setHiddenNodes] = useState<Set<string>>(new Set());
// 用于控制展开的节点
const [expandedKeys, setExpandedKeys] = useState<string[]>(['0']);
// 用于标记是否全部展开
const [isAllExpanded, setIsAllExpanded] = useState(false);
const resizeBoxRef = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器
const contextMenuRef = useRef<HTMLDivElement>(null); // 右键菜单引用
const { menuData, info, canvasDataMap } = useSelector(state => state.ideContainer);
const dispatch = useDispatch();
function getMenuData(): MenuItemType[] {
switch (identity) {
case 'scene':
const menuData = _.cloneDeep(menuData1);
const newSubMenuData = _.cloneDeep(subMenuData);
// 遍历所有 subMenuKey 来构建子菜单数据结构
Object.keys(newSubMenuData).forEach(subMenuKey => {
const subMenuValue: any = _.cloneDeep(newSubMenuData[subMenuKey]);
const menuIndex = menuData.findIndex(v => v.key === subMenuKey);
if (menuIndex !== -1) {
// 构建数据结构,这是目录默认需要的数据结构
subMenuValue.forEach(v => {
v.title = v.name;
v.key = `compFlow-${v.id}`;
v.path = 'compFlow';
v.parentKey = subMenuKey;
v.icon = '/ideContainer/icon/app.png';
if (subMenuKey !== 'appFlow') {
v.children = [
{
title: '事件',
children: null,
icon: '/ideContainer/icon/list.png'
},
{
title: '组件列表',
children: null,
icon: '/ideContainer/icon/list.png'
}
];
}
});
menuData[menuIndex]['children'] = subMenuValue;
}
});
dispatch(updateMenuData({ 'scene': menuData }));
return menuData;
case 'componentDevelopment' :
dispatch(updateMenuData({ 'componentDevelopment': menuData2 }));
return menuData2;
default:
return menuData1;
}
}
// 监听 subMenuData 和 identity 变化,更新菜单数据
useEffect(() => {
if (identity === 'scene') {
if (Object.keys(subMenuData).length > 0) {
const newMenu = getMenuData();
setMenu(newMenu);
}
}
else if (identity === 'componentDevelopment') {
const newMenu = getMenuData();
setMenu(newMenu);
}
}, [subMenuData, identity]);
// 处理子菜单区域的拖拽调整大小
const handleSubMenuResize = (e: MouseEvent, { width }: { width: number }) => {
resizeBoxRef.current.style.width = `${width}px`;
};
// 切换子菜单区域的收起/展开状态
const toggleSubMenu = () => {
setIsSubMenuCollapsed(prev => !prev);
};
// 监听子菜单收起/展开状态变化,更新宽度
useEffect(() => {
if (resizeBoxRef.current) {
const width = isSubMenuCollapsed ? 0 : 300;
resizeBoxRef.current.style.width = `${width}px`;
}
}, [isSubMenuCollapsed]);
// 获取所有可展开节点的 keys
const getAllExpandableKeys = useCallback((menuItems?: MenuItemType[], parentKey = '0'): string[] => {
const keys: string[] = [];
if (!menuItems || menuItems.length === 0) return keys;
menuItems.forEach((item, index) => {
const key = `${parentKey}-${index}`;
// 只有有子节点的才需要添加到展开列表
if (item.children && item.children.length > 0) {
keys.push(key);
// 递归获取子节点的 keys
keys.push(...getAllExpandableKeys(item.children, key));
}
});
return keys;
}, []);
// 切换展开/折叠全部
const toggleExpandAll = useCallback(() => {
if (isAllExpanded) {
// 折叠全部
setExpandedKeys([]);
setIsAllExpanded(false);
}
else {
// 展开全部
const allKeys = getAllExpandableKeys(filteredMenu[activeKey]?.children);
setExpandedKeys(allKeys);
setIsAllExpanded(true);
}
}, [isAllExpanded, activeKey, getAllExpandableKeys]);
// 处理菜单项点击事件
const handleMenuItemClick = (item: MenuItemType, index: number) => {
setActiveKey(index);
if (showSubMenu) {
// 如果点击的是当前已激活的菜单项,则切换子菜单的展开/收起状态
if ((selected?.parentKey || selected.key) === `${item.key}`) {
toggleSubMenu();
}
else {
// 如果点击的是其他菜单项,则展开子菜单(如果已收起)
if (isSubMenuCollapsed) {
setIsSubMenuCollapsed(false);
}
}
}
// 调用外部传入的菜单选择处理函数
onMenuSelect?.({ ...item });
};
// 监听 selected 状态变化,更新 activeKey
useEffect(() => {
if (menu.length > 0 && selected?.parentKey) {
// 查找匹配的菜单项索引
const index = menu.findIndex(item => item.key === selected.parentKey);
if (index !== -1) {
setActiveKey(index);
}
}
}, [selected, menu]);
// 监听 selected 状态变化,确保子菜单正确显示
useEffect(() => {
if (selected?.key && menuData[identity]) {
// 确保子菜单区域根据selected状态正确展开或收起
if (['appList', 'appFlow'].includes(selected.parentKey)) {
// // 如果应该显示子菜单但当前是收起状态,则展开
// if (isSubMenuCollapsed) {
// setIsSubMenuCollapsed(false);
// }
}
// 强制更新filteredMenu以确保显示正确的子菜单
setSearchValue(''); // 清空搜索值以显示所有项
}
}, [selected, menuData, identity]);
// 当菜单数据变化时更新本地状态
useEffect(() => {
if (identity && menuData[identity]) {
setMenu(menuData[identity]);
}
}, [menuData, identity]);
// 根据搜索值过滤菜单数据
const filteredMenu = useMemo(() => {
if (!searchValue || activeKey === undefined || !menu[activeKey]?.children) {
return menu;
}
// 深拷贝菜单数据以避免修改原始数据
const menuCopy = _.cloneDeep(menu);
// 只对当前激活的菜单项进行过滤
if (menuCopy[activeKey]?.children) {
menuCopy[activeKey].children = menuCopy[activeKey].children.filter((item: MenuItemType) => {
// 检查当前项是否匹配搜索条件
const title = item.title || '';
const isMatch = title.toLowerCase().includes(searchValue.toLowerCase());
// 如果当前项不匹配,检查其子项是否匹配
if (!isMatch && item.children && item.children.length > 0) {
item.children = item.children.filter(child => {
const childTitle = child.title || '';
return childTitle.toLowerCase().includes(searchValue.toLowerCase());
});
// 如果有匹配的子项,或者当前项匹配,则保留该项
return item.children.length > 0;
}
return isMatch;
});
}
return menuCopy;
}, [menu, searchValue, activeKey]);
const getProjectEnvData = async (data) => {
if (!data.path || !data.key || data.hasOwnProperty('compData')) return;
const parentKey = menu[activeKey]?.key;
const currentMenu = _.cloneDeep(menuData[identity]);
const index = currentMenu.findIndex(v => v.key === parentKey);
const res: any = await getAppInfoNew(data.id);
getAppEventData(data.id);
if (res.code === 200) {
const children = currentMenu[index].children.find(v => v.id === data.id);
children.children[0].children = res.data.events.map(item => {
return {
title: item,
children: null,
icon: '/ideContainer/icon/list.png'
};
});
children.children[1].children = Object.keys(res.data.compList).map(item => {
// 对于普通组件,直接渲染组件名称列表
if (item === 'appComponent') {
return {
title: compTypeMap[item],
icon: '/ideContainer/icon/app1.png',
children: res.data.compList[item].map(title => {
return {
title: title,
children: null,
icon: '/ideContainer/icon/tool.png'
};
})
};
}
// 对于复合组件,直接渲染复合组件本身(不渲染子流程)
else {
return {
title: compTypeMap[item],
icon: '/ideContainer/icon/complexApp.png',
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 => {
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;
};
// 更新 menuData 中的数据
dispatch(updateMenuData({ ...menuData, [identity]: currentMenu }));
// 更新 flowData 中的数据
dispatch(updateFlowData({ [data.id]: res.data }));
// 更新 currentAppData 中的数据
dispatch(updateCurrentAppData({ ...findMenuItem(menuData[identity], children.key) }));
dispatch(updateEventNodeList({
eventSendNodeList: [],
eventlisteneList: []
}));
// 同步更新到 canvasDataMap
if (res.data.main?.components) {
const { nodes, edges } = convertFlowData(res.data.main.components, true);
dispatch(updateCanvasDataMap({
...canvasDataMap,
[data.id]: { nodes, edges }
}));
}
// 同时更新本地 menu 状态以触发重新渲染
setMenu(prevMenu => {
const newMenu = [...prevMenu];
newMenu[activeKey] = { ...newMenu[activeKey], children: currentMenu[index].children };
return newMenu;
});
// 获取最新的事件枚举表
const res1: any = await queryEventItemBySceneIdOld(info.id);
if (res1.code === 200) dispatch(updateEventListOld(res1.data));
}
};
// 渲染子菜单
const renderMenuItems = (menuItems?: MenuItemType[], parentKey = '0') => {
if (menuItems && menuItems.length) {
return menuItems.map((item, index) => {
const key = `${parentKey}-${index}`;
const treeNodeProps = {
title: item.title,
key: key,
dataRef: item // 传递原始数据,包含 path、key 等信息
};
if (item.children && item.children.length > 0) {
return (
<TreeNode
{...treeNodeProps}
title={item.title}
key={key}
icon={item?.icon ? <img src={item.icon as string} style={{ width: 14, height: 14 }} /> : null}
>
{renderMenuItems(item.children, key)}
</TreeNode>
);
}
else {
if (item.title.includes('隐藏事件')) return null;
return (<TreeNode
{...treeNodeProps}
title={item.title}
key={key}
icon={item?.icon ? <img src={item.icon as string} style={{ width: 14, height: 14 }} /> : null}
/>);
}
});
}
return null;
};
useEffect(() => {
if (identity === 'scene') Object.keys(subMenuData).length > 0 && setMenu(getMenuData());
else if (identity === 'componentDevelopment') setMenu(getMenuData());
}, [subMenuData, identity]);
// 处理搜索输入变化
const handleSearchChange = (value: string) => {
setSearchValue(value);
};
// 处理鼠标按下事件
const handleMouseDown = (e: React.MouseEvent) => {
// 明确检查右键点击
if (e.button === 2) {
e.stopPropagation();
e.preventDefault();
// 获取点击的目标元素及其数据
const target = e.target as HTMLElement;
const treeNode = target.closest('.arco-tree-node');
if (treeNode) {
// 在实际应用中,你可能需要通过 tree-node 获取对应的数据
// 这里我们简单地显示右键菜单
}
// 设置右键菜单的位置和显示
setContextMenu({
visible: true,
x: e.clientX,
y: e.clientY,
nodeData: null
});
}
};
// 劫持右键
const handleContextMenu = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
// 设置右键菜单的位置和显示
setContextMenu({
visible: true,
x: e.clientX,
y: e.clientY,
nodeData: null
});
};
// 点击其他地方隐藏右键菜单
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (contextMenuRef.current && !contextMenuRef.current.contains(e.target as Node)) {
setContextMenu(prev => ({ ...prev, visible: false }));
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
// 监听自定义事件以更新隐藏节点状态
useEffect(() => {
const handleToggleNodeVisibility = (event: CustomEvent) => {
const { appId, isVisible } = event.detail;
if (isVisible) {
// 显示节点 - 从隐藏节点集合中移除
setHiddenNodes(prev => {
const newSet = new Set(prev);
newSet.delete(appId);
return newSet;
});
}
else {
// 隐藏节点 - 添加到隐藏节点集合
setHiddenNodes(prev => new Set(prev).add(appId));
}
};
// 监听导航到Tab的事件
const handleNavigateToTab = (event: CustomEvent) => {
const { path } = event.detail;
// 查找对应的菜单项
const menuItems = menuData[identity];
if (!menuItems) return;
const findMenuItem = (items: any[]): any => {
for (const item of items) {
if (item.path === path) {
return item;
}
if (item.children) {
const found = findMenuItem(item.children);
if (found) return found;
}
}
return null;
};
const menuItem = findMenuItem(menuItems);
if (menuItem) {
// 触发菜单选择
onMenuSelect?.({ ...menuItem });
}
};
document.addEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
document.addEventListener('navigateToTab', handleNavigateToTab as EventListener);
return () => {
document.removeEventListener('toggleNodeVisibility', handleToggleNodeVisibility as EventListener);
document.removeEventListener('navigateToTab', handleNavigateToTab as EventListener);
};
}, [menuData, identity, onMenuSelect]);
// 渲染节点的额外操作按钮
const renderNodeExtra = (node) => {
// 只有当 node.dataRef.id 存在时才渲染操作按钮
if (!node.dataRef?.id) return null;
const dropList = (
<Menu>
<MenuItem
key="editApp"
onClick={() => {
setCurrentApp(node.dataRef);
setModalType('EDIT');
setShowModal(true);
setContextMenu(prev => ({ ...prev, visible: false })); // 隐藏右键菜单
}}
>
<span style={{ color: 'rgba(161, 165, 194, 1)' }}>
<IconEdit style={{ marginRight: 6 }} />
</span>
</MenuItem>
<MenuItem
key="deleteApp"
onClick={() => {
// 使用Hook方式调用
Modal.confirm({
title: '请确认是否删除该应用?',
content: '应用删除后无法恢复,请谨慎删除!',
maskClosable: false,
cancelText: '取消',
simple: false,
closable: false,
onOk: async () => {
await deleteApp(node.dataRef.id);
// 通知父组件应用已被删除
onDeleteApp?.(node.dataRef.id);
onRefresh();
setContextMenu(prev => ({ ...prev, visible: false })); // 隐藏右键菜单
}
});
}}
>
<span style={{ color: 'rgba(255, 82, 25, 1)' }}>
<IconDelete style={{ marginRight: 6 }} />
</span>
</MenuItem>
</Menu>
);
return (
<>
{node?.dataRef?.scheduled === 1 && <div
style={{
position: 'absolute',
right: 35,
top: 8,
color: '#000000'
}}
>
<IconLoading />
</div>}
<Dropdown droplist={dropList} trigger="click">
<IconMore
style={{
position: 'absolute',
right: 8,
fontSize: 20,
fontWeight: 700,
top: 10,
color: '#000000'
}}
onClick={(e) => {
e.stopPropagation();
}}
/>
</Dropdown>
</>
);
};
const renderNodeExtraEye = (node) => {
// console.log('node:', node);
// 只有当 node.dataRef.id 存在时才渲染操作按钮
if (!node.dataRef?.id) return null;
// 检查节点当前是否被隐藏
const isNodeHidden = hiddenNodes?.has(node.dataRef.id);
function handleEyeClick(e) {
e.stopPropagation();
// 发送自定义事件,通知流程图组件隐藏/显示节点
const event = new CustomEvent('toggleNodeVisibility', {
detail: {
appId: node.dataRef.id,
isVisible: isNodeHidden // 如果当前是隐藏的,点击后应该显示
}
});
document.dispatchEvent(event);
}
return (
<div
style={{
position: 'absolute',
right: 8,
fontSize: 20,
fontWeight: 700,
top: 10,
color: '#000000',
cursor: 'pointer'
}}
onClick={handleEyeClick}
>
{isNodeHidden ? <IconEye /> : <IconEyeInvisible />}
</div>
);
};
return (
<div
className={styles['sider']}
>
<div className={styles['menu-container']}>
{menu.map((item, index) => (
<div
key={index}
className={`${styles['menu-item']} ${(selected.parentKey === `${item.parentKey}`) ? styles['menu-item-active'] : ''}`}
onClick={() => handleMenuItemClick(item, index)}
>
<div className={styles['menu-item-content']}>
{item.icon &&
<img src={selected.parentKey === `${item.parentKey}` ? item.activeIcon as string : item.icon as string}
style={{ width: 20, height: 20, marginBottom: 7 }} />}
{!item.icon && (
<span className={styles['menu-item-icon']}>
{item.icon || <IconApps />}
</span>
)}
<span className={styles['menu-item-text']}>
{item.title}
</span>
</div>
</div>
))}
</div>
{showSubMenu && <ResizeBox
ref={resizeBoxRef}
directions={['right']}
style={{
width: subMenuWidth,
height: '100%',
minHeight: 'calc(100vh - 60px)'
}}
onMoving={handleSubMenuResize}
>
<div
className={styles['sub-menu']}
>
{/* 搜索/添加 */}
{menu[activeKey]?.key === 'appList' && <div className={styles['handle-box']}>
<Input
prefix={<IconSearch />}
placeholder={'搜索'}
style={{ flex: 1 }}
value={searchValue}
onChange={handleSearchChange}
/>
<Button
type="secondary"
icon={isAllExpanded ? <IconShrink /> : <IconExpand />}
style={{ marginLeft: 5 }}
onClick={toggleExpandAll}
title={isAllExpanded ? '折叠全部' : '展开全部'}
/>
<Button
type="primary"
icon={<IconPlus />}
style={{ marginLeft: 5 }}
onClick={() => {
setShowModal(true);
setModalType('ADD');
}}
/>
</div>}
{/* 子菜单 */}
<div onContextMenu={handleContextMenu}>
<Tree
expandedKeys={expandedKeys}
onExpand={(keys) => {
setExpandedKeys(keys as string[]);
// 检查是否全部展开,更新 isAllExpanded 状态
const allKeys = getAllExpandableKeys(filteredMenu[activeKey]?.children);
setIsAllExpanded(keys.length >= allKeys.length && allKeys.length > 0);
}}
selectedKeys={[]} //
onMouseDown={handleMouseDown}
onSelect={async (_selectedKeys, info) => {
const selectedNode = info.node;
const originalData = selectedNode.props.dataRef;
if (selected?.parentKey === 'appList') {
await getProjectEnvData(originalData);
// 根据应用的 scheduled 状态更新画布的运行状态
// scheduled === 1 表示应用正在运行中
dispatch(updateIsRunning(originalData?.scheduled === 1));
// 运行中的应用同步更新runId
dispatch(updateRuntimeId(originalData?.instanceId));
// 调用外部传入的菜单选择处理函数
originalData.key && onMenuSelect?.({ ...originalData } as Selected);
}
}}
style={{ background: 'transparent' }} //
renderExtra={selected?.parentKey === 'appList' ? renderNodeExtra : renderNodeExtraEye}
>
{renderMenuItems(filteredMenu[activeKey]?.children)}
</Tree>
</div>
{/* 底部折叠按钮 */}
{!isSubMenuCollapsed && <div
className={styles['collapse-btn']}
onClick={toggleSubMenu}
title="收起侧边栏"
>
<IconLeft style={{ fontSize: 16 }} />
</div>}
</div>
</ResizeBox>}
{/* 右键菜单 */}
{/*{contextMenu.visible && (*/}
{/* <div*/}
{/* ref={contextMenuRef}*/}
{/* className={styles['context-menu']}*/}
{/* style={{*/}
{/* position: 'fixed',*/}
{/* top: contextMenu.y,*/}
{/* left: contextMenu.x,*/}
{/* zIndex: 1000*/}
{/* }}*/}
{/* >*/}
{/* <Menu*/}
{/* className={styles['context-menu-dropdown']}*/}
{/* style={{*/}
{/* borderRadius: 4,*/}
{/* boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)'*/}
{/* }}*/}
{/* >*/}
{/* <MenuItem*/}
{/* key="refresh"*/}
{/* onClick={() => {*/}
{/* console.log('点击');*/}
{/* // onRefresh();*/}
{/* // setContextMenu(prev => ({ ...prev, visible: false }));*/}
{/* }}*/}
{/* >*/}
{/* <span>编辑复合组件</span>*/}
{/* </MenuItem>*/}
{/* </Menu>*/}
{/* </div>*/}
{/*)}*/}
{/* 新增/编辑应用 */}
{showModal && (
<AppHandleModal
appInfo={currentApp}
visible={showModal}
type={modalType}
onChangeVisible={(value => setShowModal(value))}
onClose={() => setShowModal(false)}
onRefresh={onRefresh}
></AppHandleModal>
)}
</div>
);
};
export default SideBar;