Layout.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { Outlet, useNavigate, useLocation } from 'react-router-dom'
  2. import { Layout as AntLayout, Menu, Avatar, Dropdown, message } from 'antd'
  3. import {
  4. DashboardOutlined,
  5. UserOutlined,
  6. AppstoreOutlined,
  7. KeyOutlined,
  8. FileTextOutlined,
  9. LogoutOutlined,
  10. SettingOutlined,
  11. BellOutlined,
  12. DatabaseOutlined,
  13. CloudDownloadOutlined,
  14. } from '@ant-design/icons'
  15. import { useAuthStore } from '../store/authStore'
  16. import type { MenuProps } from 'antd'
  17. const { Header, Sider, Content } = AntLayout
  18. const menuItems: MenuProps['items'] = [
  19. {
  20. key: '/dashboard',
  21. icon: <DashboardOutlined />,
  22. label: '仪表盘',
  23. },
  24. {
  25. key: '/admins',
  26. icon: <UserOutlined />,
  27. label: '管理员管理',
  28. },
  29. {
  30. key: '/users',
  31. icon: <UserOutlined />,
  32. label: '用户管理',
  33. },
  34. {
  35. key: '/software',
  36. icon: <AppstoreOutlined />,
  37. label: '软件管理',
  38. },
  39. {
  40. key: '/cards',
  41. icon: <KeyOutlined />,
  42. label: '卡密管理',
  43. },
  44. {
  45. key: '/keys',
  46. icon: <SettingOutlined />,
  47. label: '密钥管理',
  48. },
  49. {
  50. key: '/logs',
  51. icon: <FileTextOutlined />,
  52. label: '日志管理',
  53. },
  54. {
  55. key: '/announcements',
  56. icon: <BellOutlined />,
  57. label: '公告管理',
  58. },
  59. {
  60. key: '/system-config',
  61. icon: <DatabaseOutlined />,
  62. label: '系统配置',
  63. },
  64. {
  65. key: '/client-versions',
  66. icon: <CloudDownloadOutlined />,
  67. label: '客户端版本',
  68. },
  69. ]
  70. export default function Layout() {
  71. const navigate = useNavigate()
  72. const location = useLocation()
  73. const { logout, adminInfo } = useAuthStore()
  74. const handleMenuClick = ({ key }: { key: string }) => {
  75. navigate(key)
  76. }
  77. const handleLogout = async () => {
  78. try {
  79. await logout()
  80. navigate('/login')
  81. message.success('退出登录成功')
  82. } catch (error) {
  83. message.error('退出登录失败')
  84. }
  85. }
  86. const userMenuItems: MenuProps['items'] = [
  87. {
  88. key: 'logout',
  89. icon: <LogoutOutlined />,
  90. label: '退出登录',
  91. onClick: handleLogout,
  92. },
  93. ]
  94. return (
  95. <AntLayout style={{ minHeight: '100vh' }}>
  96. <Header
  97. style={{
  98. padding: '0 24px',
  99. display: 'flex',
  100. justifyContent: 'space-between',
  101. alignItems: 'center',
  102. background: '#ffffff',
  103. borderBottom: '1px solid #f0f0f0',
  104. boxShadow: '0 1px 4px rgba(0, 0, 0, 0.06)',
  105. }}
  106. >
  107. <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
  108. <img
  109. src="/logo.png"
  110. alt="游铛铛"
  111. style={{
  112. height: '48px',
  113. width: 'auto',
  114. }}
  115. />
  116. <span style={{ fontSize: 18, fontWeight: 600, color: 'rgba(0, 0, 0, 0.85)' }}>
  117. 游铛铛管理端
  118. </span>
  119. </div>
  120. <Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
  121. <div style={{ cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 8 }}>
  122. <Avatar src="/avatar.jpg" />
  123. <span>{adminInfo?.real_name || adminInfo?.username || '管理员'}</span>
  124. </div>
  125. </Dropdown>
  126. </Header>
  127. {/* 主区域:左侧菜单 + 右侧内容 */}
  128. <AntLayout>
  129. <Sider
  130. width={200}
  131. theme="light"
  132. style={{
  133. borderRight: '1px solid #f0f0f0',
  134. background: '#ffffff',
  135. }}
  136. >
  137. <Menu
  138. className="sider-menu"
  139. mode="inline"
  140. selectedKeys={[location.pathname]}
  141. items={menuItems}
  142. onClick={handleMenuClick}
  143. style={{
  144. border: 'none',
  145. padding: '16px 0',
  146. }}
  147. />
  148. </Sider>
  149. <Content
  150. style={{
  151. margin: '24px',
  152. background: '#ffffff',
  153. padding: '24px',
  154. borderRadius: '8px',
  155. boxShadow: '0 1px 3px rgba(0, 0, 0, 0.1)',
  156. minHeight: 'calc(100vh - 112px)',
  157. }}
  158. >
  159. <Outlet />
  160. </Content>
  161. </AntLayout>
  162. </AntLayout>
  163. )
  164. }