feat: 拆分模型/供应商为独立路由页面,侧边栏支持 SubMenu 分组
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { MenuProps } from "antd";
|
||||
|
||||
import { Menu } from "antd";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
|
||||
import type { MenuItemConfig } from "../../../menu";
|
||||
@@ -14,23 +15,101 @@ interface SidebarProps {
|
||||
export function Sidebar({ menuItems }: SidebarProps) {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const currentPath = location.pathname;
|
||||
const currentItem = menuItems.find((item) => item.path === currentPath);
|
||||
const selectedKeys = currentItem ? [currentItem.value] : [];
|
||||
|
||||
const antdMenuItems: MenuItem[] = menuItems.map((item) => ({
|
||||
icon: item.icon,
|
||||
key: item.value,
|
||||
label: item.label,
|
||||
}));
|
||||
const rootSubmenuKeys = useMemo(() => getRootSubmenuKeys(menuItems), [menuItems]);
|
||||
const antdMenuItems = useMemo(() => toAntdMenuItems(menuItems), [menuItems]);
|
||||
|
||||
const [openKeys, setOpenKeys] = useState<string[]>(() => {
|
||||
return findAncestorKeys(menuItems, currentPath) ?? [];
|
||||
});
|
||||
|
||||
const currentItem = findMenuItem(menuItems, currentPath);
|
||||
const selectedKeys: string[] = currentItem ? [currentItem.value] : [];
|
||||
|
||||
const handleOpenChange: MenuProps["onOpenChange"] = (keys) => {
|
||||
const latestOpenKey = keys.find((key) => !openKeys.includes(key));
|
||||
if (latestOpenKey && rootSubmenuKeys.includes(latestOpenKey)) {
|
||||
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
|
||||
} else {
|
||||
setOpenKeys(keys);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuClick: MenuProps["onClick"] = ({ key }) => {
|
||||
const item = menuItems.find((i) => i.value === key);
|
||||
const item = findByValue(menuItems, key);
|
||||
if (item) {
|
||||
void navigate(item.path);
|
||||
}
|
||||
};
|
||||
|
||||
return <Menu items={antdMenuItems} mode="inline" onClick={handleMenuClick} selectedKeys={selectedKeys} />;
|
||||
return (
|
||||
<Menu
|
||||
items={antdMenuItems}
|
||||
mode="inline"
|
||||
onClick={handleMenuClick}
|
||||
onOpenChange={handleOpenChange}
|
||||
openKeys={openKeys}
|
||||
selectedKeys={selectedKeys}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function findAncestorKeys(
|
||||
items: readonly MenuItemConfig[],
|
||||
path: string,
|
||||
ancestors: string[] = [],
|
||||
): string[] | undefined {
|
||||
for (const item of items) {
|
||||
if (item.path === path) return ancestors;
|
||||
if (item.children) {
|
||||
const result = findAncestorKeys(item.children, path, [...ancestors, item.value]);
|
||||
if (result !== undefined) return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findByValue(items: readonly MenuItemConfig[], value: string): MenuItemConfig | undefined {
|
||||
for (const item of items) {
|
||||
if (item.value === value) return item;
|
||||
if (item.children) {
|
||||
const found = findByValue(item.children, value);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function findMenuItem(items: readonly MenuItemConfig[], path: string): MenuItemConfig | undefined {
|
||||
for (const item of items) {
|
||||
if (item.path === path) return item;
|
||||
if (item.children) {
|
||||
const found = findMenuItem(item.children, path);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getRootSubmenuKeys(items: readonly MenuItemConfig[]): string[] {
|
||||
return items.filter((item) => item.children).map((item) => item.value);
|
||||
}
|
||||
|
||||
function toAntdMenuItems(items: readonly MenuItemConfig[]): MenuItem[] {
|
||||
return items.map((item): MenuItem => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
return {
|
||||
children: toAntdMenuItems(item.children),
|
||||
icon: item.icon,
|
||||
key: item.value,
|
||||
label: item.label,
|
||||
};
|
||||
}
|
||||
return {
|
||||
icon: item.icon,
|
||||
key: item.value,
|
||||
label: item.label,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user