menu.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import React from 'react';
  2. import { useNavigate } from 'react-router';
  3. import { useAuth } from './hooks/AuthProvider';
  4. import {
  5. Users,
  6. Settings,
  7. User,
  8. LogOut,
  9. BarChart3,
  10. LayoutDashboard,
  11. File,
  12. Megaphone,
  13. Tag,
  14. Package,
  15. Truck,
  16. Building,
  17. UserCheck
  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. onClick?: () => void;
  27. }
  28. /**
  29. * 菜单搜索 Hook
  30. * 封装菜单搜索相关逻辑
  31. */
  32. export const useMenuSearch = (menuItems: MenuItem[]) => {
  33. const [searchText, setSearchText] = React.useState('');
  34. // 过滤菜单项
  35. const filteredMenuItems = React.useMemo(() => {
  36. if (!searchText) return menuItems;
  37. const filterItems = (items: MenuItem[]): MenuItem[] => {
  38. return items
  39. .map(item => {
  40. // 克隆对象避免修改原数据
  41. const newItem = { ...item };
  42. if (newItem.children) {
  43. newItem.children = filterItems(newItem.children);
  44. }
  45. return newItem;
  46. })
  47. .filter(item => {
  48. // 保留匹配项或其子项匹配的项
  49. const match = item.label.toLowerCase().includes(searchText.toLowerCase());
  50. if (match) return true;
  51. if (item.children?.length) return true;
  52. return false;
  53. });
  54. };
  55. return filterItems(menuItems);
  56. }, [menuItems, searchText]);
  57. // 清除搜索
  58. const clearSearch = () => {
  59. setSearchText('');
  60. };
  61. return {
  62. searchText,
  63. setSearchText,
  64. filteredMenuItems,
  65. clearSearch
  66. };
  67. };
  68. export const useMenu = () => {
  69. const navigate = useNavigate();
  70. const { logout: handleLogout } = useAuth();
  71. const [collapsed, setCollapsed] = React.useState(false);
  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: 'analytics',
  96. label: '数据分析',
  97. icon: <BarChart3 className="h-4 w-4" />,
  98. path: '/admin/analytics',
  99. permission: 'analytics:view'
  100. },
  101. {
  102. key: 'advertisements',
  103. label: '广告管理',
  104. icon: <Megaphone className="h-4 w-4" />,
  105. permission: 'advertisement:manage',
  106. children: [
  107. {
  108. key: 'advertisements-list',
  109. label: '广告列表',
  110. path: '/admin/advertisements',
  111. permission: 'advertisement:manage'
  112. },
  113. {
  114. key: 'advertisement-types',
  115. label: '广告类型',
  116. path: '/admin/advertisement-types',
  117. permission: 'advertisement:manage'
  118. }
  119. ]
  120. },
  121. {
  122. key: 'goods',
  123. label: '商品管理',
  124. icon: <Package className="h-4 w-4" />,
  125. permission: 'goods:manage',
  126. children: [
  127. {
  128. key: 'goods-list',
  129. label: '商品列表',
  130. path: '/admin/goods',
  131. permission: 'goods:manage'
  132. },
  133. {
  134. key: 'goods-categories',
  135. label: '商品分类',
  136. path: '/admin/goods-categories',
  137. permission: 'goods:manage'
  138. },
  139. {
  140. key: 'express-companies',
  141. label: '快递公司',
  142. path: '/admin/express-companies',
  143. permission: 'goods:manage'
  144. }
  145. ]
  146. },
  147. {
  148. key: 'suppliers',
  149. label: '供应商管理',
  150. icon: <Building className="h-4 w-4" />,
  151. path: '/admin/suppliers',
  152. permission: 'supplier:manage'
  153. },
  154. {
  155. key: 'agents',
  156. label: '代理商管理',
  157. icon: <UserCheck className="h-4 w-4" />,
  158. path: '/admin/agents',
  159. permission: 'agent:manage'
  160. },
  161. {
  162. key: 'settings',
  163. label: '系统设置',
  164. icon: <Settings className="h-4 w-4" />,
  165. path: '/admin/settings',
  166. permission: 'settings:manage'
  167. },
  168. ];
  169. // 用户菜单项
  170. const userMenuItems = [
  171. {
  172. key: 'profile',
  173. label: '个人资料',
  174. icon: <User className="mr-2 h-4 w-4" />,
  175. onClick: () => navigate('/admin/profile')
  176. },
  177. {
  178. key: 'settings',
  179. label: '账户设置',
  180. icon: <Settings className="mr-2 h-4 w-4" />,
  181. onClick: () => navigate('/admin/account-settings')
  182. },
  183. {
  184. type: 'separator',
  185. key: 'divider',
  186. },
  187. {
  188. key: 'logout',
  189. label: '退出登录',
  190. icon: <LogOut className="mr-2 h-4 w-4" />,
  191. onClick: () => handleLogout()
  192. }
  193. ];
  194. // 处理菜单点击
  195. const handleMenuClick = (item: MenuItem) => {
  196. if (item.path) {
  197. navigate(item.path);
  198. }
  199. if (item.onClick) {
  200. item.onClick();
  201. }
  202. };
  203. return {
  204. menuItems,
  205. userMenuItems,
  206. collapsed,
  207. setCollapsed,
  208. handleMenuClick,
  209. };
  210. };