查看种子列表用户端和管理员端界面
Change-Id: Iaa99a85c824730d993687c3af6daaeb868b220b8
diff --git a/src/components/torrentmanage.jsx b/src/components/torrentmanage.jsx
new file mode 100644
index 0000000..4756b35
--- /dev/null
+++ b/src/components/torrentmanage.jsx
@@ -0,0 +1,443 @@
+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;
\ No newline at end of file