| import React, { useState, useEffect } from 'react'; |
| import { |
| Typography, |
| Card, |
| Avatar, |
| Statistic, |
| Row, |
| Col, |
| Tag, |
| Progress, |
| Button, |
| Divider, |
| Space, |
| Tooltip, |
| message, |
| Modal, |
| Form, |
| Input, |
| Upload |
| } from 'antd'; |
| import { |
| UserOutlined, |
| EditOutlined, |
| UploadOutlined, |
| DownloadOutlined, |
| TrophyOutlined, |
| HeartOutlined, |
| WarningOutlined, |
| CheckCircleOutlined, |
| SyncOutlined, |
| GiftOutlined, |
| SettingOutlined, |
| CameraOutlined |
| } from '@ant-design/icons'; |
| import { useAuth } from '@/features/auth/contexts/AuthContext'; |
| import { getUserStats, updateUserProfile, uploadAvatar } from '@/api/user'; |
| |
| const { Title, Text, Paragraph } = Typography; |
| |
| const ProfilePage = () => { |
| const { user } = useAuth(); |
| const [loading, setLoading] = useState(false); |
| const [editModalVisible, setEditModalVisible] = useState(false); |
| const [form] = Form.useForm(); |
| |
| // PT站统计数据 |
| const [ptStats, setPtStats] = useState({ |
| uploadSize: 157.89, // GB |
| downloadSize: 89.32, // GB |
| ratio: 1.77, |
| points: 12580, |
| userClass: 'Power User', |
| seedingCount: 12, |
| leechingCount: 2, |
| completedCount: 156, |
| invites: 3, |
| warnings: 0, |
| hitAndRuns: 0 |
| }); |
| |
| // 获取用户统计信息 |
| useEffect(() => { |
| if (user?.username) { |
| fetchUserStats(); |
| } |
| }, [user]); |
| |
| const fetchUserStats = async () => { |
| try { |
| setLoading(true); |
| const response = await getUserStats(user.username); |
| if (response && response.data) { |
| setPtStats(prevStats => ({ |
| ...prevStats, |
| ...response.data |
| })); |
| } |
| } catch (error) { |
| console.error('获取用户统计信息失败:', error); |
| // 使用默认数据,不显示错误信息 |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| // 格式化文件大小 |
| const formatSize = (sizeInGB) => { |
| if (sizeInGB >= 1024) { |
| return `${(sizeInGB / 1024).toFixed(2)} TB`; |
| } |
| return `${sizeInGB.toFixed(2)} GB`; |
| }; |
| |
| // 获取分享率颜色 |
| const getRatioColor = (ratio) => { |
| if (ratio >= 2.0) return '#52c41a'; // 绿色 |
| if (ratio >= 1.0) return '#1890ff'; // 蓝色 |
| if (ratio >= 0.5) return '#faad14'; // 橙色 |
| return '#f5222d'; // 红色 |
| }; |
| |
| // 获取用户等级颜色 |
| const getUserClassColor = (userClass) => { |
| const classColors = { |
| 'User': 'default', |
| 'Power User': 'blue', |
| 'Elite User': 'purple', |
| 'Crazy User': 'gold', |
| 'Insane User': 'red', |
| 'Veteran User': 'green', |
| 'Extreme User': 'volcano', |
| 'VIP': 'magenta' |
| }; |
| return classColors[userClass] || 'default'; |
| }; |
| |
| // 显示编辑对话框 |
| const showEditModal = () => { |
| form.setFieldsValue({ |
| username: user?.username, |
| email: user?.email |
| }); |
| setEditModalVisible(true); |
| }; |
| |
| // 处理编辑提交 |
| const handleEditSubmit = async () => { |
| try { |
| const values = await form.validateFields(); |
| setLoading(true); |
| |
| const response = await updateUserProfile({ |
| username: user.username, |
| ...values |
| }); |
| |
| if (response && response.data) { |
| message.success('资料更新成功'); |
| setEditModalVisible(false); |
| // 可以触发AuthContext的用户信息更新 |
| } else { |
| message.error('更新失败,请重试'); |
| } |
| |
| } catch (error) { |
| console.error('更新失败:', error); |
| message.error(error.message || '更新失败,请重试'); |
| } finally { |
| setLoading(false); |
| } |
| }; |
| |
| // 头像上传处理 |
| const handleAvatarUpload = async (file) => { |
| const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; |
| if (!isJpgOrPng) { |
| message.error('只能上传 JPG/PNG 格式的图片!'); |
| return false; |
| } |
| const isLt2M = file.size / 1024 / 1024 < 2; |
| if (!isLt2M) { |
| message.error('图片大小不能超过 2MB!'); |
| return false; |
| } |
| |
| try { |
| const formData = new FormData(); |
| formData.append('avatar', file); |
| formData.append('username', user.username); |
| |
| const response = await uploadAvatar(formData); |
| if (response && response.data) { |
| message.success('头像上传成功'); |
| // 可以触发AuthContext的用户信息更新或重新获取用户信息 |
| } else { |
| message.error('头像上传失败'); |
| } |
| } catch (error) { |
| console.error('头像上传失败:', error); |
| message.error('头像上传失败'); |
| } |
| |
| return false; // 阻止默认上传行为 |
| }; |
| |
| // 头像上传配置 |
| const uploadProps = { |
| name: 'avatar', |
| showUploadList: false, |
| beforeUpload: handleAvatarUpload, |
| }; |
| |
| return ( |
| <div className="space-y-6"> |
| <div className="flex justify-between items-center"> |
| <Title level={2}>个人资料</Title> |
| <Button |
| type="primary" |
| icon={<EditOutlined />} |
| onClick={showEditModal} |
| > |
| 编辑资料 |
| </Button> |
| </div> |
| |
| <Row gutter={[24, 24]}> |
| {/* 用户基本信息卡片 */} |
| <Col xs={24} lg={8}> |
| <Card> |
| <div className="text-center"> |
| <div className="relative inline-block"> |
| <Avatar |
| size={120} |
| src={user?.avatar} |
| icon={<UserOutlined />} |
| className="mb-4" |
| /> |
| <Upload {...uploadProps}> |
| <Button |
| type="primary" |
| shape="circle" |
| icon={<CameraOutlined />} |
| size="small" |
| className="absolute bottom-0 right-0" |
| /> |
| </Upload> |
| </div> |
| |
| <Title level={3} className="mb-2">{user?.username || '用户'}</Title> |
| |
| <Space direction="vertical" className="w-full"> |
| <Tag |
| color={getUserClassColor(ptStats.userClass)} |
| className="text-lg px-3 py-1" |
| > |
| 用户等级{user.level} |
| </Tag> |
| |
| <Text type="secondary">邮箱:{user?.email || '未设置'}</Text> |
| </Space> |
| </div> |
| </Card> |
| </Col> |
| |
| {/* PT站统计信息 */} |
| <Col xs={24} lg={16}> |
| <Card title={ |
| <Space> |
| <TrophyOutlined /> |
| <span>PT站统计</span> |
| </Space> |
| }> |
| <Row gutter={[16, 16]}> |
| {/* 上传下载统计 */} |
| <Col xs={12} sm={6}> |
| <Statistic |
| title="上传量" |
| value={formatSize(ptStats.uploadSize)} |
| prefix={<UploadOutlined style={{ color: '#52c41a' }} />} |
| valueStyle={{ color: '#52c41a' }} |
| /> |
| </Col> |
| <Col xs={12} sm={6}> |
| <Statistic |
| title="下载量" |
| value={formatSize(ptStats.downloadSize)} |
| prefix={<DownloadOutlined style={{ color: '#1890ff' }} />} |
| valueStyle={{ color: '#1890ff' }} |
| /> |
| </Col> |
| <Col xs={12} sm={6}> |
| <Statistic |
| title="分享率" |
| value={ptStats.ratio} |
| precision={2} |
| valueStyle={{ color: getRatioColor(ptStats.ratio) }} |
| /> |
| </Col> |
| <Col xs={12} sm={6}> |
| <Statistic |
| title="积分" |
| value={ptStats.points} |
| prefix={<HeartOutlined style={{ color: '#eb2f96' }} />} |
| valueStyle={{ color: '#eb2f96' }} |
| /> |
| </Col> |
| </Row> |
| </Card> |
| </Col> |
| </Row> |
| |
| {/* 分享率进度条 */} |
| <Card title="分享率分析"> |
| <Row gutter={[24, 16]}> |
| <Col xs={24} md={12}> |
| <div className="mb-4"> |
| <Text strong>当前分享率:{ptStats.ratio}</Text> |
| <Progress |
| percent={Math.min(ptStats.ratio * 50, 100)} // 转换为百分比显示 |
| strokeColor={getRatioColor(ptStats.ratio)} |
| format={() => ptStats.ratio} |
| /> |
| </div> |
| <Space wrap> |
| <Tag color="green">≥2.0 优秀</Tag> |
| <Tag color="blue">≥1.0 良好</Tag> |
| <Tag color="orange">≥0.5 及格</Tag> |
| <Tag color="red"><0.5 需要改善</Tag> |
| </Space> |
| </Col> |
| <Col xs={24} md={12}> |
| <div className="space-y-2"> |
| <Paragraph> |
| <Text strong>分享率说明:</Text> |
| </Paragraph> |
| <Paragraph type="secondary" className="text-sm"> |
| • 分享率 = 上传量 ÷ 下载量<br/> |
| • 保持良好的分享率有助于维护账号状态<br/> |
| • 建议长期做种热门资源提升分享率<br/> |
| • 分享率过低可能导致账号受限 |
| </Paragraph> |
| </div> |
| </Col> |
| </Row> |
| </Card> |
| |
| {/* 编辑资料对话框 */} |
| <Modal |
| title="编辑个人资料" |
| open={editModalVisible} |
| onOk={handleEditSubmit} |
| onCancel={() => setEditModalVisible(false)} |
| confirmLoading={loading} |
| okText="保存" |
| cancelText="取消" |
| > |
| <Form form={form} layout="vertical"> |
| <Form.Item |
| name="username" |
| label="用户名" |
| rules={[{ required: true, message: '请输入用户名' }]} |
| > |
| <Input placeholder="请输入用户名" /> |
| </Form.Item> |
| <Form.Item |
| name="email" |
| label="邮箱" |
| rules={[ |
| { required: true, message: '请输入邮箱' }, |
| { type: 'email', message: '请输入有效的邮箱地址' } |
| ]} |
| > |
| <Input placeholder="请输入邮箱" /> |
| </Form.Item> |
| </Form> |
| </Modal> |
| </div> |
| ); |
| }; |
| |
| export default ProfilePage; |