blob: 936b88a4b2190d11b66b822afefce999039719e9 [file] [log] [blame]
BirdNETM8bcf85f2025-06-09 16:07:21 +08001// index.tsx - 活动中心页面
2
3import React, { useState, useEffect, useRef } from 'react';
4import {
5 Card,
6 Button,
7 Tag,
8 message,
9 Spin,
10 Empty,
11 List,
12 Space,
13 Typography,
14 Upload
15} from 'antd';
16import {
17 GiftOutlined,
18 UploadOutlined,
19 DownloadOutlined,
20 FireOutlined,
21 CheckCircleOutlined,
22 ClockCircleOutlined
23} from '@ant-design/icons';
24import { PageContainer } from '@ant-design/pro-components';
25import { getActivityList, participateActivity, formatDateTime } from './service';
26import { uploadTorrent, downloadTorrent } from '../Torrent/service';
27import type { SysActivity } from './data';
28
29const { Title, Text } = Typography;
30
31const ActivityPage: React.FC = () => {
32 const [activities, setActivities] = useState<SysActivity[]>([]);
33 const [loading, setLoading] = useState(false);
34 const [participatingIds, setParticipatingIds] = useState<Set<number>>(new Set());
35 const [uploading, setUploading] = useState(false);
36 const [downloading, setDownloading] = useState<number | null>(null);
37 const fileInputRef = useRef<HTMLInputElement>(null);
38
39 // 获取活动列表
40 const fetchActivities = async () => {
41 setLoading(true);
42 try {
43 const res = await getActivityList({ pageNum: 1, pageSize: 20 });
44 console.log('活动列表响应:', res);
45 if (res.code === 0) {
46 // 修正:使用 res.data.list 而不是 res.rows
47 setActivities(res.data.list);
48 console.log('活动数据:', res.data.list);
49 console.log('第一个活动:', res.data.list[0]);
50 } else {
51 message.error(res.msg || '获取活动列表失败');
52 }
53 } catch (error) {
54 console.error('获取活动列表异常:', error);
55 message.error('网络异常,请稍后重试');
56 } finally {
57 setLoading(false);
58 }
59 };
60
61 // 获取排行榜
62 // const fetchLeaderboard = async () => {
63 // try {
64 // const res = await getLeaderboard({ pageNum: 1, pageSize: 50 });
65 // if (res.code === 0) {
66 // setLeaderboard(res.data.list);
67 // } else {
68 // message.error(res.msg || '获取排行榜失败');
69 // }
70 // } catch (error) {
71 // console.error('获取排行榜失败', error);
72 // message.error('获取排行榜异常,请稍后重试');
73 // }
74 // };
75
76 // 处理文件上传
77 const handleUpload = async (file: File, activity: SysActivity) => {
78 setUploading(true);
79 try {
80 const res = await uploadTorrent(file);
81 if (res.code === 0) {
82 message.success('上传成功!');
83 // 上传成功后自动参与活动
84 await handleParticipate(activity.activityId);
85 } else {
86 message.error(res.msg || '上传失败');
87 }
88 } catch (error) {
89 console.error('上传异常:', error);
90 message.error('上传异常,请稍后重试');
91 } finally {
92 setUploading(false);
93 }
94 };
95
96 // 处理种子下载
97 const handleDownload = async (activity: SysActivity) => {
98 setDownloading(activity.activityId);
99 try {
100 const torrentId = activity.conditionValue; // 种子ID存储在conditionValue中
101 const blob = await downloadTorrent(Number(torrentId));
102
103 // 创建下载链接
104 const url = window.URL.createObjectURL(blob);
105 const link = document.createElement('a');
106 link.href = url;
107 link.download = `torrent_${torrentId}.torrent`;
108 document.body.appendChild(link);
109 link.click();
110 document.body.removeChild(link);
111 window.URL.revokeObjectURL(url);
112
113 message.success('下载成功');
114 // 下载成功后自动参与活动
115 await handleParticipate(activity.activityId);
116 } catch (error) {
117 console.error('下载异常:', error);
118 message.error('下载异常,请稍后重试');
119 } finally {
120 setDownloading(null);
121 }
122 };
123
124 // 触发文件选择
125 const triggerFileUpload = (activity: SysActivity) => {
126 const input = document.createElement('input');
127 input.type = 'file';
128 input.accept = '.torrent';
129 input.onchange = (e) => {
130 const file = (e.target as HTMLInputElement).files?.[0];
131 if (file) {
132 handleUpload(file, activity);
133 }
134 };
135 input.click();
136 };
137
138 // 参与活动
139 const handleParticipate = async (activityId: number) => {
140 // 添加到参与中的活动ID集合
141 setParticipatingIds(prev => new Set(prev).add(activityId));
142
143 try {
144 const res = await participateActivity(activityId);
145 if (res.code === 0) {
146 message.success('参与成功!');
147 fetchActivities();
148 // 已删除排行榜刷新
149 } else {
150 message.error(res.msg || '参与失败');
151 }
152 } catch (error) {
153 console.error('参与活动异常:', error);
154 message.error('网络异常,请稍后重试');
155 } finally {
156 // 从参与中的活动ID集合中移除
157 setParticipatingIds(prev => {
158 const newSet = new Set(prev);
159 newSet.delete(activityId);
160 return newSet;
161 });
162 }
163 };
164
165 useEffect(() => {
166 fetchActivities();
167 // 已删除排行榜获取
168 }, []);
169
170 // 渲染活动类型图标(可点击)
171 const renderActivityIcon = (activity: SysActivity) => {
172 if (activity.status === 0) {
173 // 活动已结束,图标不可点击
174 return activity.activityType === 'UPLOAD' ?
175 <UploadOutlined style={{ fontSize: 20, color: '#d9d9d9' }} /> :
176 <DownloadOutlined style={{ fontSize: 20, color: '#d9d9d9' }} />;
177 }
178
179 if (activity.activityType === 'UPLOAD') {
180 return (
181 <UploadOutlined
182 style={{
183 fontSize: 20,
184 color: '#1890ff',
185 cursor: 'pointer'
186 }}
187 title="点击上传种子文件"
188 onClick={() => triggerFileUpload(activity)}
189 />
190 );
191 } else {
192 return (
193 <DownloadOutlined
194 style={{
195 fontSize: 20,
196 color: '#52c41a',
197 cursor: 'pointer'
198 }}
199 title="点击下载种子"
200 onClick={() => handleDownload(activity)}
201 />
202 );
203 }
204 };
205
206 // 渲染状态标签
207 const renderStatusTag = (status: number) => {
208 return status === 1 ? (
209 <Tag icon={<FireOutlined />} color="success">进行中</Tag>
210 ) : (
211 <Tag icon={<CheckCircleOutlined />} color="default">已结束</Tag>
212 );
213 };
214
215 // 格式化条件显示
216 const formatCondition = (activity: SysActivity) => {
217 if (activity.activityType === 'UPLOAD') {
218 return `上传 ${activity.conditionValue}`;
219 }
220 return '完成下载任务';
221 };
222
223 // 格式化时间显示
224 const formatTimeRange = (startTime: string, endTime: string) => {
225 try {
226 // 处理 ISO 8601 格式的时间
227 const start = formatDateTime(startTime);
228 const end = formatDateTime(endTime);
229
230 // 只显示日期部分
231 const startDate = start.split(' ')[0];
232 const endDate = end.split(' ')[0];
233
234 return `${startDate} ~ ${endDate}`;
235 } catch (error) {
236 console.error('时间格式化错误:', error);
237 // 降级处理:如果是老格式,直接使用
238 return `${startTime.split(' ')[0]} ~ ${endTime.split(' ')[0]}`;
239 }
240 };
241
242 // 排行榜相关代码已删除
243
244 return (
245 <PageContainer title="活动中心">
246 <Spin spinning={loading}>
247 <List
248 grid={{ gutter: 16, xs: 1, sm: 2, md: 2, lg: 3, xl: 3 }}
249 dataSource={activities}
250 locale={{ emptyText: <Empty description="暂无活动" /> }}
251 renderItem={(activity) => (
252 <List.Item>
253 <Card
254 hoverable
255 actions={[
256 <Button
257 key="participate"
258 type="primary"
259 disabled={activity.status === 0}
260 loading={
261 participatingIds.has(activity.activityId) ||
262 uploading ||
263 downloading === activity.activityId
264 }
265 onClick={() => handleParticipate(activity.activityId)}
266 >
267 {activity.status === 1 ? '确认参与' : '已结束'}
268 </Button>
269 ]}
270 >
271 <Space direction="vertical" size={8} style={{ width: '100%' }}>
272 <Space style={{ width: '100%', justifyContent: 'space-between' }}>
273 {renderActivityIcon(activity)}
274 {renderStatusTag(activity.status)}
275 </Space>
276
277 <Title level={5} style={{ margin: 0 }}>
278 {activity.activityName}
279 </Title>
280
281 <Text type="secondary">
282 {formatCondition(activity)}
283 </Text>
284
285 <Space>
286 <GiftOutlined style={{ color: '#faad14' }} />
287 <Text strong style={{ color: '#faad14' }}>
288 {activity.rewardBonus} 积分
289 </Text>
290 </Space>
291
292 <Space size={4} style={{ fontSize: 12 }}>
293 <ClockCircleOutlined />
294 <Text type="secondary">
295 {formatTimeRange(activity.startTime, activity.endTime)}
296 </Text>
297 </Space>
298
299 {/* 操作提示 */}
300 {activity.status === 1 && (
301 <Text type="secondary" style={{ fontSize: 12 }}>
302 {activity.activityType === 'UPLOAD'
303 ? '点击上传图标选择种子文件'
304 : '点击下载图标获取种子'
305 }
306 </Text>
307 )}
308 </Space>
309 </Card>
310 </List.Item>
311 )}
312 />
313 </Spin>
314 </PageContainer>
315 );
316};
317
318export default ActivityPage;