feat(web): 三级可折叠侧边栏菜单 — 健康管理 18 项归入 6 个子分组
- 新增 CollapsibleSubGroup 组件,支持子分组的展开/折叠渲染 - DynamicMenuSection 改为递归检测子目录,支持多级嵌套 - 当前路径所在子分组自动展开 - 折叠侧边栏时子分组显示为图标 + Tooltip - 兼容 menu_type='page' 类型 - 数据库插入 6 个子分组(患者医护/预约排班/随访咨询/积分运营/内容运营/AI 分析)
This commit is contained in:
@@ -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 (
|
||||
<Tooltip title={directory.title} placement="right">
|
||||
<div
|
||||
onClick={() => {
|
||||
const first = visibleChildren[0];
|
||||
if (first) onNavigate(first.path || first.id);
|
||||
}}
|
||||
className={`erp-sidebar-item ${hasActive ? 'erp-sidebar-item-active' : ''}`}
|
||||
>
|
||||
<span className="erp-sidebar-item-icon">{getIcon(directory.icon)}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={`erp-sidebar-submenu-title ${hasActive ? 'erp-sidebar-submenu-title-active' : ''}`}
|
||||
onClick={() => setExpanded((e) => !e)}
|
||||
>
|
||||
<span className="erp-sidebar-submenu-arrow">
|
||||
<RightOutlined
|
||||
style={{ fontSize: 10, transition: 'transform 0.2s', transform: expanded ? 'rotate(90deg)' : 'rotate(0deg)' }}
|
||||
/>
|
||||
</span>
|
||||
<span className="erp-sidebar-submenu-label">{directory.title}</span>
|
||||
</div>
|
||||
{expanded && visibleChildren.map((child) => (
|
||||
<SidebarMenuItem
|
||||
key={child.id}
|
||||
item={{
|
||||
key: child.path || child.id,
|
||||
icon: getIcon(child.icon),
|
||||
label: child.title,
|
||||
}}
|
||||
isActive={currentPath === (child.path || child.id)}
|
||||
collapsed={collapsed}
|
||||
onClick={() => onNavigate(child.path || child.id)}
|
||||
indented
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
// 插件子菜单组
|
||||
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 (
|
||||
<div key={menu.id}>
|
||||
{!collapsed && <div className="erp-sidebar-group">{menu.title}</div>}
|
||||
<div className="erp-sidebar-menu">
|
||||
{menu.children
|
||||
?.filter((child) => child.visible !== false)
|
||||
.map((child) => (
|
||||
<SidebarMenuItem
|
||||
key={child.id}
|
||||
item={{
|
||||
key: child.path || child.id,
|
||||
icon: getIcon(child.icon),
|
||||
label: child.title,
|
||||
}}
|
||||
isActive={currentPath === (child.path || child.id)}
|
||||
collapsed={collapsed}
|
||||
onClick={() => onNavigate(child.path || child.id)}
|
||||
/>
|
||||
))}
|
||||
{visibleChildren.map((child) => {
|
||||
if (child.menu_type === 'directory') {
|
||||
return (
|
||||
<CollapsibleSubGroup
|
||||
key={child.id}
|
||||
directory={child}
|
||||
collapsed={collapsed}
|
||||
currentPath={currentPath}
|
||||
onNavigate={onNavigate}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (isLeafType(child.menu_type)) {
|
||||
return (
|
||||
<SidebarMenuItem
|
||||
key={child.id}
|
||||
item={{
|
||||
key: child.path || child.id,
|
||||
icon: getIcon(child.icon),
|
||||
label: child.title,
|
||||
}}
|
||||
isActive={currentPath === (child.path || child.id)}
|
||||
collapsed={collapsed}
|
||||
onClick={() => onNavigate(child.path || child.id)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (menu.menu_type === 'menu' && menu.visible !== false) {
|
||||
if (isLeafType(menu.menu_type) && menu.visible !== false) {
|
||||
return (
|
||||
<SidebarMenuItem
|
||||
key={menu.id}
|
||||
|
||||
Reference in New Issue
Block a user