menu.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. Ticket,
  13. Gift,
  14. QrCode,
  15. CreditCard,
  16. CreditCard as CreditCardIcon,
  17. Image
  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: 'coupon-logs',
  103. label: '领券日志',
  104. icon: <Ticket className="h-4 w-4" />,
  105. path: '/admin/coupon-logs',
  106. permission: 'coupon:view'
  107. },
  108. {
  109. key: 'marketing',
  110. label: '营销管理',
  111. icon: <Gift className="h-4 w-4" />,
  112. children: [
  113. {
  114. key: 'wechat-coupon-stocks',
  115. label: '代金券批次',
  116. icon: <CreditCard className="h-4 w-4" />,
  117. path: '/admin/wechat-coupon-stocks',
  118. permission: 'coupon:manage'
  119. },
  120. {
  121. key: 'wechat-coupons',
  122. label: '微信代金券',
  123. icon: <CreditCard className="h-4 w-4" />,
  124. path: '/admin/wechat-coupons',
  125. permission: 'coupon:view'
  126. },
  127. {
  128. key: 'redemption-codes',
  129. label: '兑换码',
  130. icon: <QrCode className="h-4 w-4" />,
  131. path: '/admin/redemption-codes',
  132. permission: 'coupon:manage'
  133. },
  134. {
  135. key: 'wechat-pay-config',
  136. label: '支付配置',
  137. icon: <CreditCard className="h-4 w-4" />,
  138. path: '/admin/wechat-pay-config',
  139. permission: 'payment:manage'
  140. },
  141. {
  142. key: 'advertisements',
  143. label: '广告管理',
  144. icon: <Image className="h-4 w-4" />,
  145. path: '/admin/advertisements',
  146. permission: 'advertisement:manage'
  147. },
  148. ]
  149. },
  150. {
  151. key: 'settings',
  152. label: '系统设置',
  153. icon: <Settings className="h-4 w-4" />,
  154. path: '/admin/settings',
  155. permission: 'settings:manage'
  156. },
  157. ];
  158. // 用户菜单项
  159. const userMenuItems = [
  160. {
  161. key: 'profile',
  162. label: '个人资料',
  163. icon: <User className="mr-2 h-4 w-4" />,
  164. onClick: () => navigate('/admin/profile')
  165. },
  166. {
  167. key: 'settings',
  168. label: '账户设置',
  169. icon: <Settings className="mr-2 h-4 w-4" />,
  170. onClick: () => navigate('/admin/account-settings')
  171. },
  172. {
  173. type: 'separator',
  174. key: 'divider',
  175. },
  176. {
  177. key: 'logout',
  178. label: '退出登录',
  179. icon: <LogOut className="mr-2 h-4 w-4" />,
  180. onClick: () => handleLogout()
  181. }
  182. ];
  183. // 处理菜单点击
  184. const handleMenuClick = (item: MenuItem) => {
  185. if (item.path) {
  186. navigate(item.path);
  187. }
  188. if (item.onClick) {
  189. item.onClick();
  190. }
  191. };
  192. return {
  193. menuItems,
  194. userMenuItems,
  195. collapsed,
  196. setCollapsed,
  197. handleMenuClick,
  198. };
  199. };