| import { Avatar, Button, Card, Col, Row, Space, Typography, Divider, Badge, Empty, Spin } from 'antd'; |
| import { UserOutlined, MailOutlined, PlusOutlined, BellOutlined, EyeOutlined, CalendarOutlined } from '@ant-design/icons'; |
| import axios from 'axios'; |
| import { useEffect, useState } from 'react'; |
| import { useAppSelector, useAppDispatch } from '../../store/hooks'; |
| import { getUserInfo } from './userSlice'; |
| |
| const { Title, Text, Paragraph } = Typography; |
| |
| // 定义 WorkResponse 接口,与后端 WorkResponse 类对应 |
| interface WorkResponse { |
| id: number; |
| title: string; |
| author: string; |
| views: number; |
| categoryId: number; |
| description: string; |
| createTime: string; |
| } |
| |
| // 模拟通知数据 |
| const mockNotifications = [ |
| { |
| id: 1, |
| title: '系统通知', |
| content: '您的作品《创意设计》获得了新的点赞!', |
| time: '2小时前', |
| type: 'like', |
| unread: true |
| }, |
| { |
| id: 2, |
| title: '评论通知', |
| content: '用户"设计师小王"评论了您的作品', |
| time: '1天前', |
| type: 'comment', |
| unread: true |
| }, |
| { |
| id: 3, |
| title: '系统消息', |
| content: '平台将于本周末进行系统维护', |
| time: '3天前', |
| type: 'system', |
| unread: false |
| } |
| ]; |
| |
| function UserHome() { |
| const userState = useAppSelector(state => state.user); |
| const dispatch = useAppDispatch(); |
| const [userWorks, setUserWorks] = useState<WorkResponse[]>([]); |
| const [loading, setLoading] = useState(false); |
| const [pageLoading, setPageLoading] = useState(true); |
| |
| // 检查token并获取用户信息 |
| useEffect(() => { |
| const initializeUser = async () => { |
| const token = localStorage.getItem('token'); |
| if (!token) { |
| // 如果没有token,重定向到登录页 |
| window.location.href = '/login'; |
| return; |
| } |
| |
| // 如果用户信息为空或状态为idle,重新获取 |
| if (!userState.username || userState.status === 'idle') { |
| try { |
| await dispatch(getUserInfo()).unwrap(); |
| } catch (error) { |
| console.error('获取用户信息失败:', error); |
| // 如果获取用户信息失败,可能token过期,重定向到登录页 |
| localStorage.removeItem('token'); |
| window.location.href = '/login'; |
| return; |
| } |
| } |
| setPageLoading(false); |
| }; |
| |
| initializeUser(); |
| }, [dispatch, userState.username, userState.status]); |
| |
| // 获取用户作品 |
| useEffect(() => { |
| const fetchUserWorks = async () => { |
| if (!userState.username) return; |
| |
| try { |
| setLoading(true); |
| const token = localStorage.getItem('token'); |
| if (token) { |
| const response = await axios.get('/api/works/works/byAuthor', { |
| headers: { |
| token: token |
| }, |
| params: { |
| author: userState.username |
| } |
| }); |
| if (response.data.code === 200) { |
| setUserWorks(response.data.data || []); |
| } else { |
| console.error('获取作品失败:', response.data.message); |
| setUserWorks([]); |
| } |
| } |
| } catch (error) { |
| console.error('获取用户作品信息失败:', error); |
| setUserWorks([]); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| // 只有当用户信息加载完成且用户名存在时才获取作品 |
| if (userState.username && userState.status === 'succeeded') { |
| fetchUserWorks(); |
| } |
| }, [userState.username, userState.status]); |
| |
| // 格式化时间显示 |
| const formatDate = (dateString: string) => { |
| return new Date(dateString).toLocaleDateString('zh-CN'); |
| }; |
| |
| // 获取分类名称 |
| const getCategoryName = (categoryId: number) => { |
| const categories: { [key: number]: string } = { |
| 1: '文学创作', |
| 2: '视觉设计', |
| 3: '音乐创作', |
| 4: '影视制作', |
| 5: '其他' |
| }; |
| return categories[categoryId] || '未分类'; |
| }; |
| |
| // 如果页面正在初始化,显示加载状态 |
| if (pageLoading || userState.status === 'loading') { |
| return ( |
| <div style={{ |
| display: 'flex', |
| justifyContent: 'center', |
| alignItems: 'center', |
| minHeight: '400px' |
| }}> |
| <Spin size="large" tip="加载用户信息中..." /> |
| </div> |
| ); |
| } |
| |
| // 如果获取用户信息失败 |
| if (userState.status === 'failed' || !userState.username) { |
| return ( |
| <div style={{ |
| display: 'flex', |
| justifyContent: 'center', |
| alignItems: 'center', |
| minHeight: '400px', |
| flexDirection: 'column' |
| }}> |
| <Empty |
| description="无法获取用户信息" |
| style={{ marginBottom: 16 }} |
| /> |
| <Button |
| type="primary" |
| onClick={() => dispatch(getUserInfo())} |
| > |
| 重新加载 |
| </Button> |
| </div> |
| ); |
| } |
| |
| return ( |
| <div className="user-home-container" style={{ padding: '0 24px' }}> |
| {/* 用户信息横栏 */} |
| <Card |
| style={{ |
| marginBottom: 24, |
| background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', |
| border: 'none' |
| }} |
| > |
| <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> |
| <Space size="large" align="center"> |
| <Avatar |
| size={80} |
| icon={<UserOutlined />} |
| style={{ backgroundColor: '#fff', color: '#667eea' }} |
| /> |
| <div> |
| <Title level={2} style={{ color: 'white', margin: 0 }}> |
| {userState.username} |
| </Title> |
| <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: '16px' }}> |
| <MailOutlined /> {userState.email || '暂无邮箱'} |
| </Text> |
| <div style={{ marginTop: 8 }}> |
| <Text style={{ color: 'rgba(255,255,255,0.6)' }}> |
| 用户ID: {userState.userid} |
| </Text> |
| </div> |
| </div> |
| </Space> |
| <div style={{ textAlign: 'center', color: 'white' }}> |
| <div style={{ fontSize: '24px', fontWeight: 'bold' }}>{userWorks.length}</div> |
| <div style={{ opacity: 0.8 }}>发布作品</div> |
| </div> |
| </div> |
| </Card> |
| |
| {/* 两栏布局 */} |
| <Row gutter={24}> |
| {/* 左侧:作品展示栏 */} |
| <Col span={16}> |
| <Card |
| title={ |
| <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> |
| <span>我的作品</span> |
| <Button |
| type="primary" |
| icon={<PlusOutlined />} |
| onClick={() => { |
| // 这里后续添加跳转到发布作品页面的逻辑 |
| console.log('跳转到发布作品页面'); |
| }} |
| > |
| 发布作品 |
| </Button> |
| </div> |
| } |
| style={{ minHeight: '600px' }} |
| > |
| {loading ? ( |
| <div style={{ textAlign: 'center', padding: '50px 0' }}> |
| <Spin size="large" tip="加载作品中..." /> |
| </div> |
| ) : userWorks.length === 0 ? ( |
| <Empty |
| description="暂无作品,快去发布第一个作品吧!" |
| style={{ padding: '50px 0' }} |
| /> |
| ) : ( |
| <Row gutter={[16, 16]}> |
| {userWorks.map((work) => ( |
| <Col span={12} key={work.id}> |
| <Card |
| hoverable |
| style={{ height: '100%' }} |
| actions={[ |
| <div key="views" style={{ color: '#666' }}> |
| <EyeOutlined /> {work.views} |
| </div>, |
| <div key="category" style={{ color: '#666' }}> |
| {getCategoryName(work.categoryId)} |
| </div> |
| ]} |
| > |
| <Card.Meta |
| title={ |
| <div style={{ |
| overflow: 'hidden', |
| textOverflow: 'ellipsis', |
| whiteSpace: 'nowrap' |
| }}> |
| {work.title} |
| </div> |
| } |
| description={ |
| <div> |
| <Paragraph |
| ellipsis={{ rows: 2 }} |
| style={{ marginBottom: 8, minHeight: '40px' }} |
| > |
| {work.description || '暂无描述'} |
| </Paragraph> |
| <div style={{ |
| display: 'flex', |
| justifyContent: 'space-between', |
| fontSize: '12px', |
| color: '#999' |
| }}> |
| <span> |
| <CalendarOutlined /> {formatDate(work.createTime)} |
| </span> |
| <span>ID: {work.id}</span> |
| </div> |
| </div> |
| } |
| /> |
| </Card> |
| </Col> |
| ))} |
| </Row> |
| )} |
| </Card> |
| </Col> |
| |
| {/* 右侧:通知栏 */} |
| <Col span={8}> |
| <Card |
| title={ |
| <div style={{ display: 'flex', alignItems: 'center' }}> |
| <BellOutlined style={{ marginRight: 8 }} /> |
| 通知中心 |
| <Badge count={2} style={{ marginLeft: 8 }} /> |
| </div> |
| } |
| style={{ minHeight: '600px' }} |
| > |
| <div> |
| {mockNotifications.map((notification, index) => ( |
| <div key={notification.id}> |
| <div |
| style={{ |
| padding: '12px', |
| backgroundColor: notification.unread ? '#f6ffed' : 'transparent', |
| borderRadius: '4px', |
| }} |
| > |
| <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}> |
| <div style={{ flex: 1 }}> |
| <div style={{ |
| fontWeight: notification.unread ? 'bold' : 'normal', |
| marginBottom: '4px', |
| display: 'flex', |
| alignItems: 'center' |
| }}> |
| {notification.title} |
| {notification.unread && ( |
| <Badge |
| color="red" |
| style={{ marginLeft: '8px' }} |
| /> |
| )} |
| </div> |
| <div style={{ |
| color: '#666', |
| fontSize: '14px', |
| lineHeight: '1.4', |
| marginBottom: '8px' |
| }}> |
| {notification.content} |
| </div> |
| <div style={{ color: '#999', fontSize: '12px' }}> |
| {notification.time} |
| </div> |
| </div> |
| </div> |
| </div> |
| {index < mockNotifications.length - 1 && <Divider style={{ margin: '0' }} />} |
| </div> |
| ))} |
| |
| {/* 查看更多按钮 */} |
| <div style={{ textAlign: 'center', marginTop: '16px' }}> |
| <Button type="link">查看全部通知</Button> |
| </div> |
| </div> |
| </Card> |
| </Col> |
| </Row> |
| </div> |
| ); |
| } |
| |
| export default UserHome; |