diff --git a/apps/web/src/layouts/MainLayout.tsx b/apps/web/src/layouts/MainLayout.tsx
index 8a6374c..972b5d8 100644
--- a/apps/web/src/layouts/MainLayout.tsx
+++ b/apps/web/src/layouts/MainLayout.tsx
@@ -132,6 +132,73 @@ const SidebarMenuItem = memo(function SidebarMenuItem({
);
});
+// 可折叠子分组(3 级菜单)
+const CollapsibleSubGroup = memo(function CollapsibleSubGroup({
+ directory,
+ collapsed,
+ currentPath,
+ onNavigate,
+}: {
+ directory: MenuInfo;
+ collapsed: boolean;
+ currentPath: string;
+ onNavigate: (key: string) => void;
+}) {
+ const [expanded, setExpanded] = useState(false);
+ const visibleChildren = directory.children?.filter((c) => c.visible !== false) || [];
+ const hasActive = visibleChildren.some((c) => currentPath === (c.path || c.id));
+
+ useEffect(() => {
+ if (hasActive) setExpanded(true);
+ }, [hasActive]);
+
+ if (collapsed) {
+ return (
+
+ {
+ const first = visibleChildren[0];
+ if (first) onNavigate(first.path || first.id);
+ }}
+ className={`erp-sidebar-item ${hasActive ? 'erp-sidebar-item-active' : ''}`}
+ >
+ {getIcon(directory.icon)}
+
+
+ );
+ }
+
+ return (
+
+
setExpanded((e) => !e)}
+ >
+
+
+
+ {directory.title}
+
+ {expanded && visibleChildren.map((child) => (
+
onNavigate(child.path || child.id)}
+ indented
+ />
+ ))}
+
+ );
+});
+
// 插件子菜单组
const SidebarSubMenu = memo(function SidebarSubMenu({
group,
@@ -195,7 +262,12 @@ const SidebarSubMenu = memo(function SidebarSubMenu({
);
});
-// 动态菜单渲染
+// 判断是否为可点击的叶子菜单类型
+function isLeafType(menuType: string): boolean {
+ return menuType === 'menu' || menuType === 'page';
+}
+
+// 动态菜单渲染(支持多级嵌套)
const DynamicMenuSection = memo(function DynamicMenuSection({
menus,
collapsed,
@@ -211,30 +283,45 @@ const DynamicMenuSection = memo(function DynamicMenuSection({
<>
{menus.map((menu) => {
if (menu.menu_type === 'directory') {
+ const visibleChildren = menu.children?.filter((c) => c.visible !== false) || [];
return (
{!collapsed &&
{menu.title}
}
- {menu.children
- ?.filter((child) => child.visible !== false)
- .map((child) => (
- onNavigate(child.path || child.id)}
- />
- ))}
+ {visibleChildren.map((child) => {
+ if (child.menu_type === 'directory') {
+ return (
+
+ );
+ }
+ if (isLeafType(child.menu_type)) {
+ return (
+ onNavigate(child.path || child.id)}
+ />
+ );
+ }
+ return null;
+ })}
);
}
- if (menu.menu_type === 'menu' && menu.visible !== false) {
+ if (isLeafType(menu.menu_type) && menu.visible !== false) {
return (