menu.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. CustomerServiceOutlined,
  11. DollarOutlined,
  12. CalendarOutlined
  13. } from '@ant-design/icons';
  14. import {
  15. FileTextOutlined,
  16. DatabaseOutlined,
  17. FileProtectOutlined,
  18. HistoryOutlined,
  19. AuditOutlined
  20. } from '@ant-design/icons';
  21. export interface MenuItem {
  22. key: string;
  23. label: string;
  24. icon?: React.ReactNode;
  25. children?: MenuItem[];
  26. path?: string;
  27. permission?: string;
  28. }
  29. /**
  30. * 菜单搜索 Hook
  31. * 封装菜单搜索相关逻辑
  32. */
  33. export const useMenuSearch = (menuItems: MenuItem[]) => {
  34. const [searchText, setSearchText] = React.useState('');
  35. // 过滤菜单项
  36. const filteredMenuItems = React.useMemo(() => {
  37. if (!searchText) return menuItems;
  38. const filterItems = (items: MenuItem[]): MenuItem[] => {
  39. return items
  40. .map(item => {
  41. // 克隆对象避免修改原数据
  42. const newItem = { ...item };
  43. if (newItem.children) {
  44. newItem.children = filterItems(newItem.children);
  45. }
  46. return newItem;
  47. })
  48. .filter(item => {
  49. // 保留匹配项或其子项匹配的项
  50. const match = item.label.toLowerCase().includes(searchText.toLowerCase());
  51. if (match) return true;
  52. if (item.children?.length) return true;
  53. return false;
  54. });
  55. };
  56. return filterItems(menuItems);
  57. }, [menuItems, searchText]);
  58. // 清除搜索
  59. const clearSearch = () => {
  60. setSearchText('');
  61. };
  62. return {
  63. searchText,
  64. setSearchText,
  65. filteredMenuItems,
  66. clearSearch
  67. };
  68. };
  69. export const useMenu = () => {
  70. const navigate = useNavigate();
  71. const { logout: handleLogout } = useAuth();
  72. const [collapsed, setCollapsed] = React.useState(false);
  73. const [openKeys, setOpenKeys] = React.useState<string[]>([]);
  74. // 基础菜单项配置
  75. const menuItems: MenuItem[] = [
  76. {
  77. key: 'dashboard',
  78. label: '控制台',
  79. icon: <DashboardOutlined />,
  80. path: '/admin/dashboard'
  81. },
  82. {
  83. key: 'users',
  84. label: '用户管理',
  85. icon: <TeamOutlined />,
  86. path: '/admin/users',
  87. permission: 'user:manage'
  88. },
  89. {
  90. key: 'crm',
  91. label: '客户关系管理',
  92. icon: <CustomerServiceOutlined />,
  93. children: [
  94. {
  95. key: 'customers-all',
  96. label: '全部客户',
  97. path: '/admin/clients',
  98. permission: 'client:manage'
  99. },
  100. {
  101. key: 'customers-pending',
  102. label: '待审核',
  103. path: '/admin/clients/pending',
  104. permission: 'client:manage'
  105. },
  106. {
  107. key: 'customers-approved',
  108. label: '已审核',
  109. path: '/admin/clients/approved',
  110. permission: 'client:manage'
  111. },
  112. {
  113. key: 'customers-rejected',
  114. label: '已拒绝',
  115. path: '/admin/clients/rejected',
  116. permission: 'client:manage'
  117. },
  118. {
  119. key: 'contacts',
  120. label: '联系人管理',
  121. path: '/admin/contacts',
  122. permission: 'contact:manage'
  123. },
  124. {
  125. key: 'order-records',
  126. label: '订单记录',
  127. path: '/admin/order-records',
  128. permission: 'order:manage'
  129. },
  130. {
  131. key: 'follow-up-records',
  132. label: '跟单记录',
  133. path: '/admin/follow-up-records',
  134. permission: 'followup:manage'
  135. },
  136. ]
  137. },
  138. {
  139. key: 'system',
  140. label: '系统管理',
  141. icon: <DatabaseOutlined />,
  142. children: [
  143. {
  144. key: 'areas',
  145. label: '区域管理',
  146. path: '/admin/areas',
  147. permission: 'area:manage'
  148. },
  149. {
  150. key: 'files',
  151. label: '文件管理',
  152. icon: <FileTextOutlined />,
  153. path: '/admin/files',
  154. permission: 'file:manage'
  155. },
  156. {
  157. key: 'logs',
  158. label: '系统日志',
  159. icon: <HistoryOutlined />,
  160. path: '/admin/logs',
  161. permission: 'log:view'
  162. }
  163. ]
  164. },
  165. {
  166. key: 'finance',
  167. label: '财务管理',
  168. icon: <FileProtectOutlined />,
  169. children: [
  170. {
  171. key: 'expenses',
  172. label: '费用管理',
  173. path: '/admin/expenses',
  174. permission: 'expense:manage'
  175. }
  176. ]
  177. },
  178. {
  179. key: 'contract',
  180. label: '合同管理',
  181. icon: <AuditOutlined />,
  182. children: [
  183. {
  184. key: 'contracts',
  185. label: '合同列表',
  186. path: '/admin/contracts',
  187. permission: 'contract:manage'
  188. },
  189. {
  190. key: 'contract-renews',
  191. label: '合同续签',
  192. path: '/admin/contract-renews',
  193. permission: 'contract:renew'
  194. }
  195. ]
  196. }
  197. ];
  198. // 用户菜单项
  199. const userMenuItems: MenuProps['items'] = [
  200. {
  201. key: 'profile',
  202. label: '个人资料',
  203. icon: <UserOutlined />
  204. },
  205. {
  206. key: 'logout',
  207. label: '退出登录',
  208. icon: <InfoCircleOutlined />,
  209. danger: true,
  210. onClick: () => handleLogout()
  211. }
  212. ];
  213. // 处理菜单点击
  214. const handleMenuClick = (item: MenuItem) => {
  215. if (item.path) {
  216. navigate(item.path);
  217. }
  218. };
  219. // 处理菜单展开变化
  220. const onOpenChange = (keys: string[]) => {
  221. const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
  222. setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
  223. };
  224. return {
  225. menuItems,
  226. userMenuItems,
  227. openKeys,
  228. collapsed,
  229. setCollapsed,
  230. handleMenuClick,
  231. onOpenChange
  232. };
  233. };