menu.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import React from 'react';
  2. import { useNavigate } from 'react-router';
  3. import { useAuth } from './hooks/AuthProvider';
  4. import type { MenuProps } from 'antd';
  5. import {
  6. UserOutlined,
  7. DashboardOutlined,
  8. TeamOutlined,
  9. InfoCircleOutlined,
  10. FileTextOutlined,
  11. } from '@ant-design/icons';
  12. export interface MenuItem {
  13. key: string;
  14. label: string;
  15. icon?: React.ReactNode;
  16. children?: MenuItem[];
  17. path?: string;
  18. permission?: string;
  19. }
  20. /**
  21. * 菜单搜索 Hook
  22. * 封装菜单搜索相关逻辑
  23. */
  24. export const useMenuSearch = (menuItems: MenuItem[]) => {
  25. const [searchText, setSearchText] = React.useState('');
  26. // 过滤菜单项
  27. const filteredMenuItems = React.useMemo(() => {
  28. if (!searchText) return menuItems;
  29. const filterItems = (items: MenuItem[]): MenuItem[] => {
  30. return items
  31. .map(item => {
  32. // 克隆对象避免修改原数据
  33. const newItem = { ...item };
  34. if (newItem.children) {
  35. newItem.children = filterItems(newItem.children);
  36. }
  37. return newItem;
  38. })
  39. .filter(item => {
  40. // 保留匹配项或其子项匹配的项
  41. const match = item.label.toLowerCase().includes(searchText.toLowerCase());
  42. if (match) return true;
  43. if (item.children?.length) return true;
  44. return false;
  45. });
  46. };
  47. return filterItems(menuItems);
  48. }, [menuItems, searchText]);
  49. // 清除搜索
  50. const clearSearch = () => {
  51. setSearchText('');
  52. };
  53. return {
  54. searchText,
  55. setSearchText,
  56. filteredMenuItems,
  57. clearSearch
  58. };
  59. };
  60. export const useMenu = () => {
  61. const navigate = useNavigate();
  62. const { logout: handleLogout } = useAuth();
  63. const [collapsed, setCollapsed] = React.useState(false);
  64. const [openKeys, setOpenKeys] = React.useState<string[]>([]);
  65. // 基础菜单项配置
  66. const menuItems: MenuItem[] = [
  67. {
  68. key: 'dashboard',
  69. label: '控制台',
  70. icon: <DashboardOutlined />,
  71. path: '/admin/dashboard'
  72. },
  73. {
  74. key: 'users',
  75. label: '用户管理',
  76. icon: <TeamOutlined />,
  77. path: '/admin/users',
  78. permission: 'user:manage'
  79. },
  80. {
  81. key: 'files',
  82. label: '文件管理',
  83. icon: <FileTextOutlined />,
  84. path: '/admin/files',
  85. permission: 'file:manage'
  86. },
  87. ];
  88. // 用户菜单项
  89. const userMenuItems: MenuProps['items'] = [
  90. {
  91. key: 'profile',
  92. label: '个人资料',
  93. icon: <UserOutlined />
  94. },
  95. {
  96. key: 'logout',
  97. label: '退出登录',
  98. icon: <InfoCircleOutlined />,
  99. danger: true,
  100. onClick: () => handleLogout()
  101. }
  102. ];
  103. // 处理菜单点击
  104. const handleMenuClick = (item: MenuItem) => {
  105. if (item.path) {
  106. navigate(item.path);
  107. }
  108. };
  109. // 处理菜单展开变化
  110. const onOpenChange = (keys: string[]) => {
  111. const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
  112. setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
  113. };
  114. return {
  115. menuItems,
  116. userMenuItems,
  117. openKeys,
  118. collapsed,
  119. setCollapsed,
  120. handleMenuClick,
  121. onOpenChange
  122. };
  123. };