2
0

menu.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. User,
  7. LayoutDashboard,
  8. Users,
  9. Info,
  10. Database,
  11. FileText,
  12. LineChart,
  13. Code,
  14. Calendar,
  15. File,
  16. MessageSquare,
  17. Video
  18. } from 'lucide-react';
  19. export interface MenuItem {
  20. key: string;
  21. label: string;
  22. icon?: React.ReactNode;
  23. children?: MenuItem[];
  24. path?: string;
  25. permission?: string;
  26. }
  27. /**
  28. * 菜单搜索 Hook
  29. * 封装菜单搜索相关逻辑
  30. */
  31. export const useMenuSearch = (menuItems: MenuItem[]) => {
  32. const [searchText, setSearchText] = React.useState('');
  33. // 过滤菜单项
  34. const filteredMenuItems = React.useMemo(() => {
  35. if (!searchText) return menuItems;
  36. const filterItems = (items: MenuItem[]): MenuItem[] => {
  37. return items
  38. .map(item => {
  39. // 克隆对象避免修改原数据
  40. const newItem = { ...item };
  41. if (newItem.children) {
  42. newItem.children = filterItems(newItem.children);
  43. }
  44. return newItem;
  45. })
  46. .filter(item => {
  47. // 保留匹配项或其子项匹配的项
  48. const match = item.label.toLowerCase().includes(searchText.toLowerCase());
  49. if (match) return true;
  50. if (item.children?.length) return true;
  51. return false;
  52. });
  53. };
  54. return filterItems(menuItems);
  55. }, [menuItems, searchText]);
  56. // 清除搜索
  57. const clearSearch = () => {
  58. setSearchText('');
  59. };
  60. return {
  61. searchText,
  62. setSearchText,
  63. filteredMenuItems,
  64. clearSearch
  65. };
  66. };
  67. export const useMenu = () => {
  68. const navigate = useNavigate();
  69. const { logout: handleLogout } = useAuth();
  70. const [collapsed, setCollapsed] = React.useState(false);
  71. const [openKeys, setOpenKeys] = React.useState<string[]>([]);
  72. // 基础菜单项配置
  73. const menuItems: MenuItem[] = [
  74. {
  75. key: 'dashboard',
  76. label: '控制台',
  77. icon: <LayoutDashboard className="h-4 w-4" />,
  78. path: '/admin/dashboard'
  79. },
  80. {
  81. key: 'users',
  82. label: '用户管理',
  83. icon: <Users className="h-4 w-4" />,
  84. path: '/admin/users',
  85. permission: 'user:manage'
  86. },
  87. {
  88. key: 'files',
  89. label: '文件管理',
  90. icon: <File className="h-4 w-4" />,
  91. path: '/admin/files',
  92. permission: 'file:manage'
  93. },
  94. {
  95. key: 'classroom-data',
  96. label: '教室数据管理',
  97. icon: <Database className="h-4 w-4" />,
  98. path: '/admin/classroom-data',
  99. permission: 'classroom:manage'
  100. },
  101. {
  102. key: 'submission-records',
  103. label: '提交记录管理',
  104. icon: <FileText className="h-4 w-4" />,
  105. path: '/admin/submission-records',
  106. permission: 'submission:manage'
  107. },
  108. {
  109. key: 'stock-data',
  110. label: '股票数据管理',
  111. icon: <LineChart className="h-4 w-4" />,
  112. path: '/admin/stock-data',
  113. permission: 'stock:manage'
  114. },
  115. {
  116. key: 'stock-xunlian-codes',
  117. label: '训练代码管理',
  118. icon: <Code className="h-4 w-4" />,
  119. path: '/admin/stock-xunlian-codes',
  120. permission: 'stock:manage'
  121. },
  122. {
  123. key: 'date-notes',
  124. label: '日期备注管理',
  125. icon: <Calendar className="h-4 w-4" />,
  126. path: '/admin/date-notes',
  127. permission: 'stock:manage'
  128. },
  129. {
  130. key: 'chat-messages',
  131. label: '聊天消息管理',
  132. icon: <MessageSquare className="h-4 w-4" />,
  133. path: '/admin/chat-messages',
  134. permission: 'chat:manage'
  135. },
  136. {
  137. key: 'vod-videos',
  138. label: '视频管理',
  139. icon: <Video className="h-4 w-4" />,
  140. path: '/admin/vod-videos',
  141. permission: 'vod:manage'
  142. }
  143. ];
  144. // 用户菜单项
  145. const userMenuItems: MenuProps['items'] = [
  146. {
  147. key: 'profile',
  148. label: '个人资料',
  149. icon: <User className="h-4 w-4" />
  150. },
  151. {
  152. key: 'logout',
  153. label: '退出登录',
  154. icon: <Info className="h-4 w-4" />,
  155. danger: true,
  156. onClick: () => handleLogout()
  157. }
  158. ];
  159. // 处理菜单点击
  160. const handleMenuClick = (item: MenuItem) => {
  161. if (item.path) {
  162. navigate(item.path);
  163. }
  164. };
  165. // 处理菜单展开变化
  166. const onOpenChange = (keys: string[]) => {
  167. const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
  168. setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
  169. };
  170. return {
  171. menuItems,
  172. userMenuItems,
  173. openKeys,
  174. collapsed,
  175. setCollapsed,
  176. handleMenuClick,
  177. onOpenChange
  178. };
  179. };