blob: cae4f6d97957aa0ea98a8da9ad3d0a6a024c55b7 [file] [log] [blame]
22301021313d1b22025-06-09 01:13:46 +08001import { Avatar, Button, Card, Col, Row, Space, Typography, Divider, Badge, Empty, Spin } from 'antd';
2import { UserOutlined, MailOutlined, PlusOutlined, BellOutlined, EyeOutlined, CalendarOutlined } from '@ant-design/icons';
3import axios from 'axios';
4import { useEffect, useState } from 'react';
5import { useAppSelector, useAppDispatch } from '../../store/hooks';
22301014356527a2025-06-09 17:46:56 +08006import { getUserDetail, getUserInfo } from './userSlice';
22301021313d1b22025-06-09 01:13:46 +08007
8const { Title, Text, Paragraph } = Typography;
9
10// 定义 WorkResponse 接口,与后端 WorkResponse 类对应
11interface WorkResponse {
12 id: number;
13 title: string;
14 author: string;
15 views: number;
16 categoryId: number;
17 description: string;
18 createTime: string;
19}
20
21// 模拟通知数据
22const mockNotifications = [
23 {
24 id: 1,
25 title: '系统通知',
26 content: '您的作品《创意设计》获得了新的点赞!',
27 time: '2小时前',
28 type: 'like',
29 unread: true
30 },
31 {
32 id: 2,
33 title: '评论通知',
34 content: '用户"设计师小王"评论了您的作品',
35 time: '1天前',
36 type: 'comment',
37 unread: true
38 },
39 {
40 id: 3,
41 title: '系统消息',
42 content: '平台将于本周末进行系统维护',
43 time: '3天前',
44 type: 'system',
45 unread: false
46 }
47];
48
49function UserHome() {
50 const userState = useAppSelector(state => state.user);
51 const dispatch = useAppDispatch();
52 const [userWorks, setUserWorks] = useState<WorkResponse[]>([]);
53 const [loading, setLoading] = useState(false);
54 const [pageLoading, setPageLoading] = useState(true);
55
56 // 检查token并获取用户信息
57 useEffect(() => {
58 const initializeUser = async () => {
22301014356527a2025-06-09 17:46:56 +080059 if (userState.userid) {
60 await dispatch(getUserDetail(userState.userid));
61
22301021313d1b22025-06-09 01:13:46 +080062 }
63
64 // 如果用户信息为空或状态为idle,重新获取
65 if (!userState.username || userState.status === 'idle') {
66 try {
22301014356527a2025-06-09 17:46:56 +080067 await dispatch(getUserInfo()).then(
68 () => {
69 dispatch(getUserDetail(userState.userid));
70 }
71 )
22301021313d1b22025-06-09 01:13:46 +080072 } catch (error) {
73 console.error('获取用户信息失败:', error);
74 // 如果获取用户信息失败,可能token过期,重定向到登录页
75 localStorage.removeItem('token');
76 window.location.href = '/login';
77 return;
78 }
79 }
22301014356527a2025-06-09 17:46:56 +080080
22301021313d1b22025-06-09 01:13:46 +080081 setPageLoading(false);
82 };
83
84 initializeUser();
85 }, [dispatch, userState.username, userState.status]);
86
87 // 获取用户作品
88 useEffect(() => {
89 const fetchUserWorks = async () => {
90 if (!userState.username) return;
91
92 try {
93 setLoading(true);
94 const token = localStorage.getItem('token');
95 if (token) {
96 const response = await axios.get('/api/works/works/byAuthor', {
97 headers: {
98 token: token
99 },
100 params: {
101 author: userState.username
102 }
103 });
104 if (response.data.code === 200) {
105 setUserWorks(response.data.data || []);
106 } else {
107 console.error('获取作品失败:', response.data.message);
108 setUserWorks([]);
109 }
110 }
111 } catch (error) {
112 console.error('获取用户作品信息失败:', error);
113 setUserWorks([]);
114 } finally {
115 setLoading(false);
116 }
117 };
118
119 // 只有当用户信息加载完成且用户名存在时才获取作品
120 if (userState.username && userState.status === 'succeeded') {
121 fetchUserWorks();
122 }
123 }, [userState.username, userState.status]);
124
125 // 格式化时间显示
126 const formatDate = (dateString: string) => {
127 return new Date(dateString).toLocaleDateString('zh-CN');
128 };
129
130 // 获取分类名称
131 const getCategoryName = (categoryId: number) => {
132 const categories: { [key: number]: string } = {
133 1: '文学创作',
134 2: '视觉设计',
135 3: '音乐创作',
136 4: '影视制作',
137 5: '其他'
138 };
139 return categories[categoryId] || '未分类';
140 };
141
142 // 如果页面正在初始化,显示加载状态
143 if (pageLoading || userState.status === 'loading') {
144 return (
22301014356527a2025-06-09 17:46:56 +0800145 <div style={{
146 display: 'flex',
147 justifyContent: 'center',
148 alignItems: 'center',
149 minHeight: '400px'
22301021313d1b22025-06-09 01:13:46 +0800150 }}>
151 <Spin size="large" tip="加载用户信息中..." />
152 </div>
153 );
154 }
155
156 // 如果获取用户信息失败
157 if (userState.status === 'failed' || !userState.username) {
158 return (
22301014356527a2025-06-09 17:46:56 +0800159 <div style={{
160 display: 'flex',
161 justifyContent: 'center',
162 alignItems: 'center',
22301021313d1b22025-06-09 01:13:46 +0800163 minHeight: '400px',
164 flexDirection: 'column'
165 }}>
22301014356527a2025-06-09 17:46:56 +0800166 <Empty
22301021313d1b22025-06-09 01:13:46 +0800167 description="无法获取用户信息"
168 style={{ marginBottom: 16 }}
169 />
22301014356527a2025-06-09 17:46:56 +0800170 <Button
171 type="primary"
22301021313d1b22025-06-09 01:13:46 +0800172 onClick={() => dispatch(getUserInfo())}
173 >
174 重新加载
175 </Button>
176 </div>
177 );
178 }
179
22301014356527a2025-06-09 17:46:56 +0800180
181
182
22301021313d1b22025-06-09 01:13:46 +0800183 return (
184 <div className="user-home-container" style={{ padding: '0 24px' }}>
185 {/* 用户信息横栏 */}
22301014356527a2025-06-09 17:46:56 +0800186 <Card
187 style={{
188 marginBottom: 24,
22301021313d1b22025-06-09 01:13:46 +0800189 background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
190 border: 'none'
191 }}
192 >
193 <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
194 <Space size="large" align="center">
22301014356527a2025-06-09 17:46:56 +0800195 <Avatar
196 size={80}
197 icon={<UserOutlined />}
22301021313d1b22025-06-09 01:13:46 +0800198 style={{ backgroundColor: '#fff', color: '#667eea' }}
199 />
200 <div>
201 <Title level={2} style={{ color: 'white', margin: 0 }}>
202 {userState.username}
203 </Title>
204 <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: '16px' }}>
205 <MailOutlined /> {userState.email || '暂无邮箱'}
206 </Text>
207 <div style={{ marginTop: 8 }}>
208 <Text style={{ color: 'rgba(255,255,255,0.6)' }}>
209 用户ID: {userState.userid}
210 </Text>
211 </div>
212 </div>
213 </Space>
214 <div style={{ textAlign: 'center', color: 'white' }}>
215 <div style={{ fontSize: '24px', fontWeight: 'bold' }}>{userWorks.length}</div>
216 <div style={{ opacity: 0.8 }}>发布作品</div>
217 </div>
218 </div>
219 </Card>
220
221 {/* 两栏布局 */}
222 <Row gutter={24}>
223 {/* 左侧:作品展示栏 */}
224 <Col span={16}>
22301014356527a2025-06-09 17:46:56 +0800225 <Card
22301021313d1b22025-06-09 01:13:46 +0800226 title={
227 <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
228 <span>我的作品</span>
22301014356527a2025-06-09 17:46:56 +0800229 <Button
230 type="primary"
22301021313d1b22025-06-09 01:13:46 +0800231 icon={<PlusOutlined />}
232 onClick={() => {
233 // 这里后续添加跳转到发布作品页面的逻辑
234 console.log('跳转到发布作品页面');
235 }}
236 >
237 发布作品
238 </Button>
239 </div>
240 }
241 style={{ minHeight: '600px' }}
242 >
243 {loading ? (
244 <div style={{ textAlign: 'center', padding: '50px 0' }}>
245 <Spin size="large" tip="加载作品中..." />
246 </div>
247 ) : userWorks.length === 0 ? (
22301014356527a2025-06-09 17:46:56 +0800248 <Empty
22301021313d1b22025-06-09 01:13:46 +0800249 description="暂无作品,快去发布第一个作品吧!"
250 style={{ padding: '50px 0' }}
251 />
252 ) : (
253 <Row gutter={[16, 16]}>
254 {userWorks.map((work) => (
255 <Col span={12} key={work.id}>
22301014356527a2025-06-09 17:46:56 +0800256 <Card
22301021313d1b22025-06-09 01:13:46 +0800257 hoverable
258 style={{ height: '100%' }}
259 actions={[
260 <div key="views" style={{ color: '#666' }}>
261 <EyeOutlined /> {work.views}
262 </div>,
263 <div key="category" style={{ color: '#666' }}>
264 {getCategoryName(work.categoryId)}
265 </div>
266 ]}
267 >
268 <Card.Meta
269 title={
22301014356527a2025-06-09 17:46:56 +0800270 <div style={{
271 overflow: 'hidden',
272 textOverflow: 'ellipsis',
273 whiteSpace: 'nowrap'
22301021313d1b22025-06-09 01:13:46 +0800274 }}>
275 {work.title}
276 </div>
277 }
278 description={
279 <div>
22301014356527a2025-06-09 17:46:56 +0800280 <Paragraph
281 ellipsis={{ rows: 2 }}
22301021313d1b22025-06-09 01:13:46 +0800282 style={{ marginBottom: 8, minHeight: '40px' }}
283 >
284 {work.description || '暂无描述'}
285 </Paragraph>
22301014356527a2025-06-09 17:46:56 +0800286 <div style={{
287 display: 'flex',
288 justifyContent: 'space-between',
289 fontSize: '12px',
290 color: '#999'
22301021313d1b22025-06-09 01:13:46 +0800291 }}>
292 <span>
293 <CalendarOutlined /> {formatDate(work.createTime)}
294 </span>
295 <span>ID: {work.id}</span>
296 </div>
297 </div>
298 }
299 />
300 </Card>
301 </Col>
302 ))}
303 </Row>
304 )}
305 </Card>
306 </Col>
307
308 {/* 右侧:通知栏 */}
309 <Col span={8}>
22301014356527a2025-06-09 17:46:56 +0800310 <Card
22301021313d1b22025-06-09 01:13:46 +0800311 title={
312 <div style={{ display: 'flex', alignItems: 'center' }}>
313 <BellOutlined style={{ marginRight: 8 }} />
314 通知中心
315 <Badge count={2} style={{ marginLeft: 8 }} />
316 </div>
317 }
318 style={{ minHeight: '600px' }}
319 >
320 <div>
321 {mockNotifications.map((notification, index) => (
322 <div key={notification.id}>
22301014356527a2025-06-09 17:46:56 +0800323 <div
324 style={{
22301021313d1b22025-06-09 01:13:46 +0800325 padding: '12px',
326 backgroundColor: notification.unread ? '#f6ffed' : 'transparent',
327 borderRadius: '4px',
328 }}
329 >
330 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
331 <div style={{ flex: 1 }}>
22301014356527a2025-06-09 17:46:56 +0800332 <div style={{
22301021313d1b22025-06-09 01:13:46 +0800333 fontWeight: notification.unread ? 'bold' : 'normal',
334 marginBottom: '4px',
335 display: 'flex',
336 alignItems: 'center'
337 }}>
338 {notification.title}
339 {notification.unread && (
22301014356527a2025-06-09 17:46:56 +0800340 <Badge
341 color="red"
22301021313d1b22025-06-09 01:13:46 +0800342 style={{ marginLeft: '8px' }}
343 />
344 )}
345 </div>
22301014356527a2025-06-09 17:46:56 +0800346 <div style={{
347 color: '#666',
348 fontSize: '14px',
22301021313d1b22025-06-09 01:13:46 +0800349 lineHeight: '1.4',
350 marginBottom: '8px'
351 }}>
352 {notification.content}
353 </div>
354 <div style={{ color: '#999', fontSize: '12px' }}>
355 {notification.time}
356 </div>
357 </div>
358 </div>
359 </div>
360 {index < mockNotifications.length - 1 && <Divider style={{ margin: '0' }} />}
361 </div>
362 ))}
22301014356527a2025-06-09 17:46:56 +0800363
22301021313d1b22025-06-09 01:13:46 +0800364 {/* 查看更多按钮 */}
365 <div style={{ textAlign: 'center', marginTop: '16px' }}>
366 <Button type="link">查看全部通知</Button>
367 </div>
368 </div>
369 </Card>
370 </Col>
371 </Row>
372 </div>
373 );
374}
375
376export default UserHome;