| import React, { useState, useEffect } from 'react'; |
| import { useNavigate } from 'react-router-dom'; |
| import { |
| Table, |
| Button, |
| Modal, |
| Image, |
| message, |
| Spin, |
| Input, |
| Select, |
| Pagination, |
| Space |
| } from 'antd'; |
| import { ExclamationCircleOutlined } from '@ant-design/icons'; |
| import axios from 'axios'; |
| |
| const { confirm } = Modal; |
| const { Option } = Select; |
| |
| const TorrentManagement = () => { |
| // 状态管理 |
| const [torrents, setTorrents] = useState([]); |
| const [isLoading, setIsLoading] = useState(false); |
| const [error, setError] = useState(null); |
| const [selectedTorrentId, setSelectedTorrentId] = useState(null); |
| const [promotionOptions, setPromotionOptions] = useState([ |
| { value: 1, label: '上传加倍' }, |
| { value: 2, label: '下载减半' }, |
| { value: 3, label: '免费下载' }, |
| { value: 0, label: '无促销' } |
| ]); |
| const [selectedPromotion, setSelectedPromotion] = useState(null); |
| const [showPromotionWarning, setShowPromotionWarning] = useState(false); |
| const [currentUserId, setCurrentUserId] = useState(null); |
| const [applyPromotionsLoading, setApplyPromotionsLoading] = useState(false); |
| const [usernames, setUsernames] = useState({}); |
| const [searchKeyword, setSearchKeyword] = useState(''); |
| const [currentPage, setCurrentPage] = useState(1); |
| const [pageSize, setPageSize] = useState(10); |
| const navigate = useNavigate(); // 用于导航到详情页 |
| |
| // 获取当前用户ID |
| useEffect(() => { |
| const userId = 1; // 示例,实际从认证系统获取 |
| setCurrentUserId(userId ? parseInt(userId) : null); |
| }, []); |
| |
| // 获取所有种子数据 |
| useEffect(() => { |
| fetchAllTorrents(); |
| }, [searchKeyword]); |
| |
| // 获取所有种子数据的函数 |
| const fetchAllTorrents = async () => { |
| setIsLoading(true); |
| setError(null); |
| try { |
| const res = await axios.get('http://localhost:8080/torrent/list'); |
| setTorrents(res.data); |
| setCurrentPage(1); // 重置为第一页 |
| } catch (err) { |
| console.error('获取种子失败', err); |
| setError('获取种子列表失败,请稍后重试'); |
| message.error('获取种子列表失败'); |
| } finally { |
| setIsLoading(false); |
| } |
| }; |
| console.log('当前种子列表:', torrents); |
| |
| // 在组件加载时,批量获取所有 uploader_id 对应的 username |
| useEffect(() => { |
| const fetchUsernames = async () => { |
| if (torrents.length === 0) return; |
| |
| const usernamePromises = torrents.map(async (torrent) => { |
| if (torrent.uploader_id && !usernames[torrent.uploader_id]) { |
| try { |
| const response = await fetch(`http://localhost:8080/torrent/${torrent.uploader_id}/username`); |
| if (response.ok) { |
| const username = await response.text(); |
| return { [torrent.uploader_id]: username }; |
| } |
| } catch (error) { |
| console.error(`Failed to fetch username for uploader_id ${torrent.uploader_id}:`, error); |
| } |
| } |
| return {}; |
| }); |
| |
| const results = await Promise.all(usernamePromises); |
| const mergedUsernames = results.reduce((acc, curr) => ({ ...acc, ...curr }), {}); |
| setUsernames((prev) => ({ ...prev, ...mergedUsernames })); |
| }; |
| |
| fetchUsernames(); |
| }, [torrents]); |
| |
| // 处理删除种子 |
| const handleDeleteTorrent = async (torrentId) => { |
| if (!currentUserId) { |
| message.warning('请先登录'); |
| return; |
| } |
| |
| confirm({ |
| title: '确认删除', |
| icon: <ExclamationCircleOutlined />, |
| content: '确定要删除这个种子吗?此操作不可恢复!', |
| onOk: async () => { |
| try { |
| await axios.delete(`http://localhost:8080/torrent/delete/${torrentId}`, { |
| params: { userid: currentUserId } |
| }); |
| setTorrents(torrents.filter(torrent => torrent.torrentid !== torrentId)); |
| message.success('种子删除成功'); |
| } catch (err) { |
| console.error('删除种子失败', err); |
| if (err.response && err.response.status === 403) { |
| message.error('无权删除此种子'); |
| } else { |
| message.error('删除种子失败'); |
| } |
| } |
| } |
| }); |
| }; |
| |
| // 搜索种子 |
| const handleSearch = async () => { |
| if (!searchKeyword.trim()) { |
| fetchAllTorrents(); |
| return; |
| } |
| |
| setIsLoading(true); |
| setError(null); |
| try { |
| const res = await axios.get(`http://localhost:8080/torrent/search`, { |
| params: { keyword: searchKeyword }, |
| }); |
| setTorrents(res.data); |
| setCurrentPage(1); // 搜索后重置为第一页 |
| } catch (err) { |
| console.error('搜索失败', err); |
| setError('搜索失败,请稍后重试'); |
| message.error('搜索失败'); |
| } finally { |
| setIsLoading(false); |
| } |
| }; |
| |
| // 处理修改促销方式 |
| const handlePromotionChange = (torrentId, newPromotion) => { |
| setSelectedTorrentId(torrentId); |
| setSelectedPromotion(newPromotion); |
| setShowPromotionWarning(true); |
| }; |
| |
| // 确认修改促销方式 |
| const confirmPromotionChange = async () => { |
| if (selectedTorrentId && selectedPromotion !== null) { |
| try { |
| await axios.post('http://localhost:8080/torrent/setPromotion', null, { |
| params: { |
| userid: currentUserId, |
| torrentId: selectedTorrentId, |
| promotionId: selectedPromotion |
| } |
| }); |
| setTorrents(torrents.map(torrent => |
| torrent.torrentid === selectedTorrentId |
| ? { ...torrent, promotionid: selectedPromotion } |
| : torrent |
| )); |
| setShowPromotionWarning(false); |
| message.success('促销方式修改成功'); |
| } catch (err) { |
| console.error('修改促销方式失败', err); |
| if (err.response && err.response.status === 403) { |
| message.error('无权修改此种子的促销方式'); |
| } else { |
| message.error('修改促销方式失败'); |
| } |
| } |
| } |
| }; |
| |
| // 取消修改促销方式 |
| const cancelPromotionChange = () => { |
| setShowPromotionWarning(false); |
| setSelectedTorrentId(null); |
| setSelectedPromotion(null); |
| }; |
| |
| // 触发检查(应用促销规则) |
| const handleApplyPromotions = async () => { |
| if (!currentUserId) { |
| message.warning('请先登录'); |
| return; |
| } |
| |
| setApplyPromotionsLoading(true); |
| try { |
| const res = await axios.post('http://localhost:8080/torrent/applyPromotions', null, { |
| params: { userid: currentUserId } |
| }); |
| |
| if (res.data.success) { |
| message.success(res.data.message); |
| fetchAllTorrents(); // 刷新种子列表 |
| } |
| } catch (err) { |
| console.error('应用促销规则失败', err); |
| if (err.response && err.response.status === 403) { |
| message.error('无权执行此操作'); |
| } else { |
| message.error('应用促销规则失败'); |
| } |
| } finally { |
| setApplyPromotionsLoading(false); |
| } |
| }; |
| |
| // 分页数据计算 |
| const getCurrentPageData = () => { |
| const start = (currentPage - 1) * pageSize; |
| const end = start + pageSize; |
| return torrents.slice(start, end); |
| }; |
| |
| // 页码变化处理 |
| const handlePageChange = (page) => { |
| setCurrentPage(page); |
| }; |
| |
| // 每页条数变化处理 |
| const handlePageSizeChange = (current, size) => { |
| setPageSize(size); |
| setCurrentPage(1); // 重置为第一页 |
| }; |
| |
| // 格式化日期 |
| const formatDate = (dateString) => { |
| const options = { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }; |
| return new Date(dateString).toLocaleString('zh-CN', options); |
| }; |
| |
| // 格式化文件大小 |
| const formatFileSize = (bytes) => { |
| if (bytes === 0) return '0 Bytes'; |
| const k = 1024; |
| const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
| }; |
| |
| // 获取促销方式名称 |
| const getPromotionName = (promotionId) => { |
| if (promotionId === null) return '无促销'; |
| const option = promotionOptions.find(opt => opt.value === promotionId); |
| return option ? option.label : '未知促销'; |
| }; |
| |
| const handleViewDetails = (torrentId) => { |
| navigate(`/admin/${torrentId}`); // 使用已定义的 navigate 变量 |
| }; |
| |
| return ( |
| <div className="p-4 max-w-7xl mx-auto"> |
| <h1 className="text-2xl font-bold mb-6">种子管理</h1> |
| |
| {/* 搜索框 */} |
| <div className="mb-4 flex items-center"> |
| <Input |
| placeholder="搜索种子..." |
| value={searchKeyword} |
| onChange={(e) => setSearchKeyword(e.target.value)} |
| style={{ width: 300 }} |
| onPressEnter={handleSearch} |
| /> |
| <Button |
| type="primary" |
| onClick={handleSearch} |
| style={{ marginLeft: 8 }} |
| > |
| 搜索 |
| </Button> |
| </div> |
| |
| {/* 右上角按钮 */} |
| <Button |
| type="primary" |
| loading={applyPromotionsLoading} |
| onClick={handleApplyPromotions} |
| style={{ marginBottom: 16 }} |
| > |
| 触发检查 |
| </Button> |
| |
| {/* 加载状态 */} |
| {isLoading && <Spin size="large" style={{ display: 'block', margin: '100px auto' }} />} |
| |
| {/* 错误提示 */} |
| {error && <div className="mb-4 p-3 bg-red-100 text-red-700 rounded border border-red-200">{error}</div>} |
| |
| {/* 种子列表表格 */} |
| {!isLoading && !error && ( |
| <> |
| <Table |
| columns={[ |
| { |
| title: '封面', |
| dataIndex: 'coverImagePath', |
| key: 'coverImagePath', |
| render: (text) => text ? ( |
| <Image |
| src={text} |
| width={50} |
| height={50} |
| preview={{ maskClosable: true }} |
| /> |
| ) : ( |
| <div className="w-16 h-16 bg-gray-200 flex items-center justify-center">无封面</div> |
| ) |
| }, |
| { |
| title: '名称', |
| dataIndex: 'filename', |
| key: 'filename' |
| }, |
| { |
| title: '描述', |
| dataIndex: 'description', |
| key: 'description' |
| }, |
| { |
| title: '大小', |
| dataIndex: 'torrentSize', |
| key: 'torrentSize', |
| render: (size) => formatFileSize(size) |
| }, |
| { |
| title: '上传者', |
| dataIndex: 'uploader_id', |
| key: 'uploader_id', |
| render: (id) => usernames[id] || id |
| }, |
| { |
| title: '上传时间', |
| dataIndex: 'uploadTime', |
| key: 'uploadTime', |
| render: (time) => formatDate(time) |
| }, |
| { |
| title: '下载次数', |
| dataIndex: 'downloadCount', |
| key: 'downloadCount' |
| }, |
| { |
| title: '促销', |
| dataIndex: 'promotionid', |
| key: 'promotionid', |
| render: (id) => getPromotionName(id) |
| }, |
| { |
| title: '操作', |
| key: 'action', |
| render: (_, record) => ( |
| <Space> |
| <Button |
| danger |
| onClick={() => handleDeleteTorrent(record.torrentid)} |
| loading={isLoading} |
| > |
| 删除 |
| </Button> |
| <Select |
| value={record.promotionid} |
| onChange={(value) => handlePromotionChange(record.torrentid, value)} |
| style={{ width: 120 }} |
| disabled={isLoading} |
| > |
| {promotionOptions.map(option => ( |
| <Option key={option.value} value={option.value}>{option.label}</Option> |
| ))} |
| </Select> |
| <Button |
| type="primary" |
| size="small" |
| onClick={() => handleViewDetails(record.torrentid)} // 使用处理函数 |
| > |
| 查看详情 |
| </Button> |
| </Space> |
| ) |
| } |
| ]} |
| dataSource={getCurrentPageData()} |
| rowKey="torrentid" |
| pagination={false} |
| loading={isLoading} |
| /> |
| |
| {/* 分页控件 */} |
| {torrents.length > 0 && ( |
| <div style={{ marginTop: 16, textAlign: 'center' }}> |
| <Pagination |
| current={currentPage} |
| pageSize={pageSize} |
| total={torrents.length} |
| onChange={handlePageChange} |
| onShowSizeChange={handlePageSizeChange} |
| showSizeChanger |
| showTotal={(total) => `共 ${total} 条记录`} |
| pageSizeOptions={['10', '20', '50']} |
| /> |
| </div> |
| )} |
| </> |
| )} |
| |
| {/* 促销方式修改确认弹窗 */} |
| <Modal |
| title="确认修改促销方式" |
| open={showPromotionWarning} |
| onOk={confirmPromotionChange} |
| onCancel={cancelPromotionChange} |
| okText="确认" |
| cancelText="取消" |
| > |
| <p> |
| 您确定要将种子 ID 为 |
| <span className="font-bold">{selectedTorrentId}</span> 的促销方式修改为 |
| <span className="font-bold">「{getPromotionName(selectedPromotion)}」</span> 吗? |
| </p> |
| </Modal> |
| </div> |
| ); |
| }; |
| |
| export default TorrentManagement; |