menu.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. } from 'lucide-react';
  18. export interface MenuItem {
  19. key: string;
  20. label: string;
  21. icon?: React.ReactNode;
  22. children?: MenuItem[];
  23. path?: string;
  24. permission?: string;
  25. onClick?: () => void;
  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. // 基础菜单项配置
  72. const menuItems: MenuItem[] = [
  73. {
  74. key: 'dashboard',
  75. label: '控制台',
  76. icon: <LayoutDashboard className="h-4 w-4" />,
  77. path: '/admin/dashboard'
  78. },
  79. {
  80. key: 'users',
  81. label: '用户管理',
  82. icon: <Users className="h-4 w-4" />,
  83. path: '/admin/users',
  84. permission: 'user:manage'
  85. },
  86. {
  87. key: 'files',
  88. label: '文件管理',
  89. icon: <File className="h-4 w-4" />,
  90. path: '/admin/files',
  91. permission: 'file:manage'
  92. },
  93. {
  94. key: 'analytics',
  95. label: '数据分析',
  96. icon: <BarChart3 className="h-4 w-4" />,
  97. path: '/admin/analytics',
  98. permission: 'analytics:view'
  99. },
  100. {
  101. key: 'advertisements',
  102. label: '广告管理',
  103. icon: <Megaphone className="h-4 w-4" />,
  104. permission: 'advertisement:manage',
  105. children: [
  106. {
  107. key: 'advertisements-list',
  108. label: '广告列表',
  109. path: '/admin/advertisements',
  110. permission: 'advertisement:manage'
  111. },
  112. {
  113. key: 'advertisement-types',
  114. label: '广告类型',
  115. path: '/admin/advertisement-types',
  116. permission: 'advertisement:manage'
  117. }
  118. ]
  119. },
  120. {
  121. key: 'goods',
  122. label: '商品管理',
  123. icon: <Package className="h-4 w-4" />,
  124. permission: 'goods:manage',
  125. children: [
  126. {
  127. key: 'goods-list',
  128. label: '商品列表',
  129. path: '/admin/goods',
  130. permission: 'goods:manage'
  131. },
  132. {
  133. key: 'goods-categories',
  134. label: '商品分类',
  135. path: '/admin/goods-categories',
  136. permission: 'goods:manage'
  137. },
  138. {
  139. key: 'express-companies',
  140. label: '快递公司',
  141. path: '/admin/express-companies',
  142. permission: 'goods:manage'
  143. }
  144. ]
  145. },
  146. {
  147. key: 'suppliers',
  148. label: '供应商管理',
  149. icon: <Building className="h-4 w-4" />,
  150. path: '/admin/suppliers',
  151. permission: 'supplier:manage'
  152. },
  153. {
  154. key: 'settings',
  155. label: '系统设置',
  156. icon: <Settings className="h-4 w-4" />,
  157. path: '/admin/settings',
  158. permission: 'settings:manage'
  159. },
  160. ];
  161. // 用户菜单项
  162. const userMenuItems = [
  163. {
  164. key: 'profile',
  165. label: '个人资料',
  166. icon: <User className="mr-2 h-4 w-4" />,
  167. onClick: () => navigate('/admin/profile')
  168. },
  169. {
  170. key: 'settings',
  171. label: '账户设置',
  172. icon: <Settings className="mr-2 h-4 w-4" />,
  173. onClick: () => navigate('/admin/account-settings')
  174. },
  175. {
  176. type: 'separator',
  177. key: 'divider',
  178. },
  179. {
  180. key: 'logout',
  181. label: '退出登录',
  182. icon: <LogOut className="mr-2 h-4 w-4" />,
  183. onClick: () => handleLogout()
  184. }
  185. ];
  186. // 处理菜单点击
  187. const handleMenuClick = (item: MenuItem) => {
  188. if (item.path) {
  189. navigate(item.path);
  190. }
  191. if (item.onClick) {
  192. item.onClick();
  193. }
  194. };
  195. return {
  196. menuItems,
  197. userMenuItems,
  198. collapsed,
  199. setCollapsed,
  200. handleMenuClick,
  201. };
  202. };