blob: 93cef487e60da3f52462a14d3c6dd973957f8dc5 [file] [log] [blame]
223010144ce05872025-06-08 22:33:28 +08001// src/AppLayout.tsx
2
3import React, { useEffect, useMemo } from 'react';
22301014bc4616f2025-06-03 16:59:44 +08004import { Outlet, useLocation, useNavigate } from 'react-router';
223010144ce05872025-06-08 22:33:28 +08005import { Layout, Menu, Dropdown, Button, Flex, Space } from 'antd';
6import {
7 HomeOutlined,
8 AppstoreOutlined,
9 DownOutlined,
10 UserOutlined,
11 SettingOutlined,
12 LogoutOutlined
13} from '@ant-design/icons';
22301014b1477f72025-06-07 22:54:40 +080014import logo from "./assets/logo.png";
15import { useAppDispatch, useAppSelector } from './store/hooks';
16import { getUserInfo } from './feature/user/userSlice';
17import { logout } from './feature/auth/authSlice';
22301014bc4616f2025-06-03 16:59:44 +080018
223010144ce05872025-06-08 22:33:28 +080019const { Header, Content, Footer } = Layout;
20
21const AppLayout: React.FC = () => {
22301014bc4616f2025-06-03 16:59:44 +080022 const location = useLocation();
23 const navigate = useNavigate();
22301014b1477f72025-06-07 22:54:40 +080024 const dispatch = useAppDispatch();
25
223010144ce05872025-06-08 22:33:28 +080026 // Redux状态
27 const { isAuth } = useAppSelector(state => state.auth);
28 const userState = useAppSelector(state => state.user);
22301014b1477f72025-06-07 22:54:40 +080029
223010144ce05872025-06-08 22:33:28 +080030 // 获取用户信息
31 useEffect(() => {
32 if (isAuth) {
33 dispatch(getUserInfo());
34 }
35 }, [dispatch, isAuth]);
36
37 // 判断是否在认证页面
22301014b1477f72025-06-07 22:54:40 +080038 const isAuthPage = useMemo(() => {
223010144ce05872025-06-08 22:33:28 +080039 const authPaths = ['/login', '/register', '/forget'];
40 return authPaths.includes(location.pathname);
22301014b1477f72025-06-07 22:54:40 +080041 }, [location.pathname]);
42
22301014bc4616f2025-06-03 16:59:44 +080043 // 导航项配置
44 const menuItems = [
45 {
46 key: 'home',
47 label: '主页',
48 icon: <HomeOutlined />,
49 path: '/',
50 },
51 {
223010144ce05872025-06-08 22:33:28 +080052 key: 'works',
53 label: '作品',
22301014bc4616f2025-06-03 16:59:44 +080054 icon: <AppstoreOutlined />,
223010144ce05872025-06-08 22:33:28 +080055 path: '/works',
22301014bc4616f2025-06-03 16:59:44 +080056 },
57 ];
58
223010144ce05872025-06-08 22:33:28 +080059 // 获取当前选中的菜单项
60 const selectedKey = useMemo(() => {
61 if (location.pathname === '/') return 'home';
62 if (location.pathname.startsWith('/works')) return 'works';
63 return '';
64 }, [location.pathname]);
65
22301014b1477f72025-06-07 22:54:40 +080066 // 处理登出逻辑
67 const handleLogout = () => {
223010144ce05872025-06-08 22:33:28 +080068 dispatch(logout());
69 navigate('/login', { replace: true });
22301014b1477f72025-06-07 22:54:40 +080070 };
71
72 // 下拉菜单内容
73 const dropdownMenuItems = [
74 {
75 key: 'profile',
22301021313d1b22025-06-09 01:13:46 +080076 label: '个人主页',
223010144ce05872025-06-08 22:33:28 +080077 icon: <UserOutlined />,
22301021313d1b22025-06-09 01:13:46 +080078 onClick: () => navigate('/user'),
22301014b1477f72025-06-07 22:54:40 +080079 },
80 {
223010144ce05872025-06-08 22:33:28 +080081 key: 'settings',
82 label: '设置',
83 icon: <SettingOutlined />,
84 onClick: () => navigate('/settings'),
85 },
86 {
87 type: 'divider' as const,
88 },
89 {
22301014b1477f72025-06-07 22:54:40 +080090 key: 'logout',
91 label: '登出',
223010144ce05872025-06-08 22:33:28 +080092 icon: <LogoutOutlined />,
22301014b1477f72025-06-07 22:54:40 +080093 onClick: handleLogout,
223010144ce05872025-06-08 22:33:28 +080094 danger: true,
22301014b1477f72025-06-07 22:54:40 +080095 },
96 ];
97
223010144ce05872025-06-08 22:33:28 +080098 // 处理菜单点击
99 const handleMenuClick = (path: string) => {
100 navigate(path);
101 };
102
22301014bc4616f2025-06-03 16:59:44 +0800103 return (
104 <Layout style={{ minHeight: '100vh', width: '100%' }}>
223010144ce05872025-06-08 22:33:28 +0800105 <Header
106 style={{
107 display: 'flex',
108 alignItems: 'center',
109 justifyContent: 'space-between',
110 padding: '0 24px',
111 background: '#001529'
112 }}
113 >
114 {/* Logo区域 */}
115 <Flex
116 align="center"
117 style={{ cursor: 'pointer' }}
118 onClick={() => navigate('/')}
119 >
120 <img
121 src={logo}
122 alt="创驿Logo"
123 style={{ height: 48 }}
124 />
125 <div style={{
126 color: 'white',
127 marginLeft: 12,
128 fontSize: 18,
129 fontWeight: 'bold'
130 }}>
22301014b1477f72025-06-07 22:54:40 +0800131 创驿
132 </div>
133 </Flex>
134
223010144ce05872025-06-08 22:33:28 +0800135 {/* 分割线 */}
22301014b1477f72025-06-07 22:54:40 +0800136 {!isAuthPage && (
223010144ce05872025-06-08 22:33:28 +0800137 <div style={{
138 height: 30,
139 width: 1,
140 backgroundColor: 'rgba(255, 255, 255, 0.3)',
141 margin: '0 20px',
142 }} />
143 )}
144
145 {/* 导航菜单 */}
146 {!isAuthPage && (
147 <Menu
148 mode="horizontal"
149 theme="dark"
150 selectedKeys={selectedKey ? [selectedKey] : []}
151 items={menuItems.map(item => ({
152 ...item,
153 onClick: () => handleMenuClick(item.path),
154 }))}
155 style={{
156 flex: 1,
157 minWidth: 0,
158 backgroundColor: 'transparent',
159 borderBottom: 'none'
160 }}
161 />
162 )}
163
164 {/* 用户信息区域 */}
165 {!isAuthPage && isAuth && (
166 <Space size="middle">
167 <span style={{ color: 'white' }}>
168 欢迎,{userState.username || '用户'}
169 </span>
22301014b1477f72025-06-07 22:54:40 +0800170 <Dropdown
22301014b1477f72025-06-07 22:54:40 +0800171 menu={{ items: dropdownMenuItems }}
223010144ce05872025-06-08 22:33:28 +0800172 trigger={['click']}
173 placement="bottomRight"
22301014b1477f72025-06-07 22:54:40 +0800174 >
223010144ce05872025-06-08 22:33:28 +0800175 <Button
176 type="text"
177 style={{
178 color: 'white',
179 display: 'flex',
180 alignItems: 'center'
181 }}
182 >
183 <DownOutlined />
184 </Button>
22301014b1477f72025-06-07 22:54:40 +0800185 </Dropdown>
223010144ce05872025-06-08 22:33:28 +0800186 </Space>
22301014b1477f72025-06-07 22:54:40 +0800187 )}
22301014bc4616f2025-06-03 16:59:44 +0800188 </Header>
223010144ce05872025-06-08 22:33:28 +0800189
190 <Content style={{
191 padding: isAuthPage ? 0 : 24,
192 minHeight: 'calc(100vh - 64px - 70px)' // 减去header和footer高度
193 }}>
22301014bc4616f2025-06-03 16:59:44 +0800194 <Outlet />
223010144ce05872025-06-08 22:33:28 +0800195 </Content>
196
197 {!isAuthPage && (
198 <Footer style={{
199 textAlign: 'center',
200 background: '#f0f2f5',
201 padding: '24px 50px'
202 }}>
203 © 2025 创驿 - 愿做你创作路上的同行者
204 </Footer>
205 )}
22301014bc4616f2025-06-03 16:59:44 +0800206 </Layout>
207 );
208};
209
223010144ce05872025-06-08 22:33:28 +0800210export default AppLayout;