刘嘉昕 | 8a3c6ab | 2025-06-09 17:44:13 +0800 | [diff] [blame] | 1 | import React, { useState, useEffect } from 'react'; |
| 2 | import { useParams, useNavigate, useLocation } from 'react-router-dom'; |
| 3 | import { useSearchParams } from 'react-router-dom'; |
| 4 | import { |
| 5 | Descriptions, |
| 6 | Table, |
| 7 | Button, |
| 8 | Modal, |
| 9 | Image, |
| 10 | message, |
| 11 | Spin, |
| 12 | Input, |
| 13 | Select, |
| 14 | Pagination, |
| 15 | Space, |
| 16 | Card |
| 17 | } from 'antd'; |
| 18 | import { ExclamationCircleOutlined } from '@ant-design/icons'; |
| 19 | import axios from 'axios'; |
| 20 | |
| 21 | const { confirm } = Modal; |
| 22 | const { Option } = Select; |
| 23 | |
| 24 | //const { confirm } = Modal; |
| 25 | |
| 26 | const TorrentDetailcomplain = () => { |
| 27 | const { id } = useParams(); // 从URL获取种子ID |
| 28 | const navigate = useNavigate(); // 用于返回上一页 |
| 29 | const [torrent, setTorrent] = useState(null); |
| 30 | const [loading, setLoading] = useState(true); |
| 31 | const [error, setError] = useState(null); |
| 32 | const [isLoading, setIsLoading] = useState(false); |
| 33 | //const [searchParams] = useSearchParams(); |
| 34 | const location = useLocation(); // 添加这行获取location对象 |
| 35 | // 在组件状态中添加 |
| 36 | // 获取参数并设置默认值 |
| 37 | //const [manageId, setManageId] = useState(parseInt(searchParams.get('manageid')) || 1); |
| 38 | // 正确从location.state获取参数 |
| 39 | const { duser, torrentid } = location.state || {}; |
| 40 | const [rewardUserId, setRewardUserId] = useState(duser); |
| 41 | const [creditAmount, setCreditAmount] = useState(3); |
| 42 | const [torrentId, setTorrentId] = useState(torrentid); // 使用state中的torrentid或URL中的id |
| 43 | |
| 44 | console.log('Torrent ID:', torrentId); // 调试输出 |
| 45 | console.log('Reward User ID:', rewardUserId); // 调试输出 |
| 46 | const currentUserId = 1; // 示例,实际应从认证系统获取 |
| 47 | |
| 48 | |
| 49 | // 格式化日期 |
| 50 | const formatDate = (dateString) => { |
| 51 | if (!dateString) return '未知'; |
| 52 | const date = new Date(dateString); |
| 53 | return date.toLocaleString('zh-CN', { |
| 54 | year: 'numeric', |
| 55 | month: '2-digit', |
| 56 | day: '2-digit', |
| 57 | hour: '2-digit', |
| 58 | minute: '2-digit' |
| 59 | }); |
| 60 | }; |
| 61 | |
| 62 | // 处理删除种子 |
| 63 | const handleDeleteTorrent = async (torrentId) => { |
| 64 | if (!currentUserId) { |
| 65 | message.warning('请先登录'); |
| 66 | return; |
| 67 | } |
| 68 | |
| 69 | confirm({ |
| 70 | title: '确认删除', |
| 71 | icon: <ExclamationCircleOutlined />, |
| 72 | content: '确定要删除这个种子吗?此操作不可恢复!', |
| 73 | onOk: async () => { |
| 74 | try { |
| 75 | await axios.delete(`http://localhost:8080/torrent/delete/${torrentId}`, { |
| 76 | params: { userid: currentUserId } |
| 77 | }); |
| 78 | // 成功删除后,更新状态或返回上一页 |
| 79 | setTorrent(null); // 清空当前种子详情 |
| 80 | navigate(-1); // 返回上一页 |
| 81 | message.success('种子删除成功'); |
| 82 | } catch (err) { |
| 83 | console.error('删除种子失败', err); |
| 84 | if (err.response && err.response.status === 403) { |
| 85 | message.error('无权删除此种子'); |
| 86 | } else { |
| 87 | message.error('删除种子失败'); |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | }); |
| 92 | }; |
| 93 | |
| 94 | |
| 95 | const handleDownloadTorrent = async (torrentId) => { |
| 96 | if (!currentUserId) { |
| 97 | message.warning('请先登录'); |
| 98 | return; |
| 99 | } |
| 100 | |
| 101 | setIsLoading(true); // 开始加载 |
| 102 | try { |
| 103 | // 使用axios发送带有参数的GET请求 |
| 104 | // const response = await axios.get(`http://localhost:8080/torrent/download/${torrentId}`, { |
| 105 | // params: { userId: currentUserId }, // 正确添加请求参数 |
| 106 | // responseType: 'blob' // 重要:指定响应类型为blob以处理文件下载 |
| 107 | // }); |
| 108 | |
| 109 | // // 创建下载链接 |
| 110 | // const url = window.URL.createObjectURL(new Blob([response.data])); |
| 111 | // const link = document.createElement('a'); |
| 112 | // link.href = url; |
| 113 | // //link.setAttribute('download', 'torrent_file.torrent'); // 可以设置为动态文件名 |
| 114 | // document.body.appendChild(link); |
| 115 | // link.click(); |
| 116 | // document.body.removeChild(link); |
| 117 | // window.URL.revokeObjectURL(url); |
| 118 | open(`http://localhost:8080/torrent/download/${torrentId}?userId=${currentUserId}`, '_blank'); |
| 119 | |
| 120 | message.success('种子下载开始'); |
| 121 | } catch (err) { |
| 122 | console.error('下载种子失败', err); |
| 123 | if (err.response?.status === 404) { |
| 124 | message.error('种子不存在'); |
| 125 | } else { |
| 126 | message.error('下载失败: ' + (err.response?.data?.message || err.message)); |
| 127 | } |
| 128 | } finally { |
| 129 | setIsLoading(false); // 结束加载 |
| 130 | } |
| 131 | }; |
| 132 | |
| 133 | |
| 134 | // 格式化文件大小 |
| 135 | const formatFileSize = (bytes) => { |
| 136 | if (bytes === 0) return '0 Bytes'; |
| 137 | const k = 1024; |
| 138 | const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; |
| 139 | const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| 140 | return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
| 141 | }; |
| 142 | |
| 143 | // 在组件函数内部添加这个函数 |
| 144 | const getPromotionName = (promotionId) => { |
| 145 | const promotionMap = { |
| 146 | 1: '上传加倍', |
| 147 | 2: '下载减半', |
| 148 | 3: '免费下载', |
| 149 | 0: '无促销' |
| 150 | }; |
| 151 | |
| 152 | return promotionMap[promotionId] || '未知促销'; |
| 153 | }; |
| 154 | |
| 155 | // 获取种子详情 |
| 156 | useEffect(() => { |
| 157 | const fetchTorrentDetail = async () => { |
| 158 | try { |
| 159 | const response = await axios.get(`http://localhost:8080/torrent/${torrentId}`); |
| 160 | if (response.status === 200) { |
| 161 | setTorrent(response.data); |
| 162 | } else { |
| 163 | setError('获取种子详情失败'); |
| 164 | } |
| 165 | } catch (err) { |
| 166 | console.error('获取种子详情失败:', err); |
| 167 | if (err.response) { |
| 168 | if (err.response.status === 404) { |
| 169 | setError('种子不存在'); |
| 170 | } else { |
| 171 | setError('获取种子详情失败: ' + err.response.data); |
| 172 | } |
| 173 | } else { |
| 174 | setError('网络错误,请稍后重试'); |
| 175 | } |
| 176 | } finally { |
| 177 | setLoading(false); |
| 178 | } |
| 179 | }; |
| 180 | |
| 181 | fetchTorrentDetail(); |
| 182 | }, [id]); |
| 183 | |
| 184 | // 奖励保种积分处理函数(修正版) |
| 185 | const handleAddCredit = async (torrentId) => { |
| 186 | if (!currentUserId) { // 需要定义manageUserId |
| 187 | message.warning('缺少必要权限'); |
| 188 | return; |
| 189 | } |
| 190 | |
| 191 | setIsLoading(true); |
| 192 | try { |
| 193 | const params = new URLSearchParams(); |
| 194 | params.append('manageid', currentUserId); |
| 195 | params.append('userid', rewardUserId); |
| 196 | params.append('credit', creditAmount); |
| 197 | |
| 198 | const response = await axios.post(`http://localhost:8080/torrent/deducecredit`, params.toString(), { |
| 199 | headers: { |
| 200 | 'Content-Type': 'application/x-www-form-urlencoded' |
| 201 | } |
| 202 | }); |
| 203 | |
| 204 | if (response.data.success !== false) { |
| 205 | message.success(`成功扣除 ${creditAmount} 保种积分`); |
| 206 | } else { |
| 207 | message.error(response.data.message || '奖励积分失败'); |
| 208 | } |
| 209 | } catch (err) { |
| 210 | console.error('奖励积分失败', err); |
| 211 | if (err.response?.status === 500) { |
| 212 | message.error('服务器错误: ' + (err.response.data?.message || '未知错误')); |
| 213 | } else { |
| 214 | message.error('奖励积分失败: ' + (err.response?.data?.message || err.message)); |
| 215 | } |
| 216 | } finally { |
| 217 | setIsLoading(false); |
| 218 | } |
| 219 | }; |
| 220 | |
| 221 | console.log('Torrent Detail:', torrent); |
| 222 | |
| 223 | // 返回上一页 |
| 224 | const handleBack = () => { |
| 225 | navigate(-1); // 返回上一页 |
| 226 | }; |
| 227 | |
| 228 | // 如果正在加载 |
| 229 | if (loading) { |
| 230 | return ( |
| 231 | <div className="flex justify-center items-center h-96"> |
| 232 | <Spin size="large" tip="加载中..." /> |
| 233 | </div> |
| 234 | ); |
| 235 | } |
| 236 | |
| 237 | // 如果有错误 |
| 238 | if (error) { |
| 239 | return ( |
| 240 | <div className="p-6"> |
| 241 | <Card> |
| 242 | <div className="text-center p-6"> |
| 243 | <ExclamationCircleOutlined className="text-2xl text-red-500 mb-4" /> |
| 244 | <h3 className="text-lg font-medium text-red-600 mb-2">错误</h3> |
| 245 | <p className="text-gray-600">{error}</p> |
| 246 | <Button type="primary" onClick={handleBack} className="mt-4"> |
| 247 | 返回 |
| 248 | </Button> |
| 249 | </div> |
| 250 | </Card> |
| 251 | </div> |
| 252 | ); |
| 253 | } |
| 254 | |
| 255 | // 如果种子不存在 |
| 256 | if (!torrent) { |
| 257 | return ( |
| 258 | <div className="p-6"> |
| 259 | <Card> |
| 260 | <div className="text-center p-6"> |
| 261 | <ExclamationCircleOutlined className="text-2xl text-yellow-500 mb-4" /> |
| 262 | <h3 className="text-lg font-medium text-yellow-600 mb-2">种子不存在</h3> |
| 263 | <p className="text-gray-600">抱歉,您查找的种子不存在或已被删除。</p> |
| 264 | <Button type="primary" onClick={handleBack} className="mt-4"> |
| 265 | 返回 |
| 266 | </Button> |
| 267 | </div> |
| 268 | </Card> |
| 269 | </div> |
| 270 | ); |
| 271 | } |
| 272 | |
| 273 | // 渲染种子详情 |
| 274 | return ( |
| 275 | <div className="p-6"> |
| 276 | <Card |
| 277 | title="种子详情" |
| 278 | extra={ |
| 279 | <Button type="primary" onClick={handleBack}> |
| 280 | 返回列表 |
| 281 | </Button> |
| 282 | } |
| 283 | > |
| 284 | {/* 使用Descriptions组件展示详情 */} |
| 285 | <Descriptions bordered column={1}> |
| 286 | <Descriptions.Item label="ID">{torrent.torrentid}</Descriptions.Item> |
| 287 | {/* 新增的封面图片展示 */} |
| 288 | {torrent.coverImagePath && ( |
| 289 | <Descriptions.Item label="封面图片"> |
| 290 | <Image |
| 291 | src={torrent.coverImagePath} |
| 292 | alt="种子封面" |
| 293 | width={200} // 设置图片宽度 |
| 294 | placeholder={ |
| 295 | <div className="w-48 h-32 bg-gray-200 flex items-center justify-center"> |
| 296 | 加载中... |
| 297 | </div> |
| 298 | } |
| 299 | /> |
| 300 | </Descriptions.Item> |
| 301 | )} |
| 302 | <Descriptions.Item label="文件名">{torrent.filename}</Descriptions.Item> |
| 303 | <Descriptions.Item label="大小">{formatFileSize(torrent.torrentSize)}</Descriptions.Item> |
| 304 | <Descriptions.Item label="上传者ID">{torrent.uploader_id}</Descriptions.Item> |
| 305 | <Descriptions.Item label="上传时间">{formatDate(torrent.uploadTime)}</Descriptions.Item> |
| 306 | <Descriptions.Item label="下载次数">{torrent.downloadCount}</Descriptions.Item> |
| 307 | <Descriptions.Item label="促销"> |
| 308 | {getPromotionName(torrent.promotionid)} |
| 309 | </Descriptions.Item> |
| 310 | <Descriptions.Item label="描述"> |
| 311 | {torrent.description || '无描述'} |
| 312 | </Descriptions.Item> |
| 313 | </Descriptions> |
| 314 | |
| 315 | |
| 316 | {/* 操作按钮区域 */} |
| 317 | <div className="mt-6 flex justify-end space-x-4"> |
| 318 | <Button |
| 319 | danger |
| 320 | onClick={() => handleDeleteTorrent(torrent.torrentid)} |
| 321 | loading={isLoading} |
| 322 | > |
| 323 | 删除 |
| 324 | </Button> |
| 325 | <Button |
| 326 | type="primary" |
| 327 | onClick={() => handleDownloadTorrent(torrent.torrentid)} |
| 328 | loading={isLoading} |
| 329 | > |
| 330 | 下载 |
| 331 | </Button> |
| 332 | {/* 新增的奖励保种积分按钮 */} |
| 333 | <Button |
| 334 | type="default" |
| 335 | onClick={() => handleAddCredit(torrent.torrentid)} |
| 336 | loading={isLoading} |
| 337 | > |
| 338 | 扣除保种积分 |
| 339 | </Button> |
| 340 | </div> |
| 341 | </Card> |
| 342 | </div> |
| 343 | ); |
| 344 | }; |
| 345 | |
| 346 | export default TorrentDetailcomplain; |