Merge "修复种子列表搜索排序"
diff --git a/src/pages/PublishSeed/PublishSeed.jsx b/src/pages/PublishSeed/PublishSeed.jsx
index 6528b0d..2f5bddb 100644
--- a/src/pages/PublishSeed/PublishSeed.jsx
+++ b/src/pages/PublishSeed/PublishSeed.jsx
@@ -92,25 +92,31 @@
formData.append('coverImage', imageFile);
}
- try {
+ try {
console.log('[DEBUG] 发送上传请求...');
- const response = await axios.post('/seeds/upload', formData, {
- // axios 会自动处理 multipart/form-data Content-Type 边界,不用手动设置
- // headers: { 'Content-Type': 'multipart/form-data' },
- });
+ const response = await axios.post('/seeds/upload', formData);
+
console.log('[DEBUG] 请求成功,响应:', response.data);
if (response.data.code === 0) {
setMessage('种子上传成功');
+ } else if (response.data.code === 401) {
+ setMessage('未登录或登录已过期,请重新登录');
} else {
setMessage(response.data.message || '上传失败,请稍后再试');
}
} catch (error) {
console.error('[handleSubmit] 上传失败:', error);
- setMessage('上传失败,发生了错误');
+
+ if (error.response?.status === 401 || error.response?.data?.code === 401) {
+ setMessage('未登录或登录已过期,请重新登录');
+ } else {
+ setMessage(error.response?.data?.msg || '上传失败,发生了错误');
+ }
} finally {
setIsLoading(false);
}
+
};
return (
diff --git a/src/pages/SeedList/SeedList.css b/src/pages/SeedList/SeedList.css
index cdbf651..a850dfd 100644
--- a/src/pages/SeedList/SeedList.css
+++ b/src/pages/SeedList/SeedList.css
@@ -216,4 +216,8 @@
border-radius: 8px;
margin-bottom: 12px;
box-shadow: 0 4px 12px rgb(107 79 59 / 0.2);
-}
\ No newline at end of file
+}
+a {
+ color: inherit; /* 使用父元素颜色 */
+ text-decoration: none; /* 去掉下划线 */
+}
diff --git a/src/pages/SeedList/SeedList.jsx b/src/pages/SeedList/SeedList.jsx
index 45b9f8c..18a0587 100644
--- a/src/pages/SeedList/SeedList.jsx
+++ b/src/pages/SeedList/SeedList.jsx
@@ -40,9 +40,18 @@
'种子列表': '',
};
+ const formatBytes = (bytes) => {
+ if (bytes === 0 || bytes === null || bytes === undefined) return '0 B';
+ const k = 1024;
+ const sizes = ['B', '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 buildQueryParams = () => {
const category = CATEGORY_MAP[activeTab] || '';
- const orderKey = sortOption === '最新' ? 'upload_time' : (sortOption === '最热' ? 'downloads' : 'upload_time');
+ const orderKey = sortOption === '最新' ? 'create_time' : (sortOption === '最热' ? 'leechers' : 'create_time');
const params = {
page: 1,
size: 20,
@@ -51,7 +60,7 @@
};
if (searchTerm.trim()) {
- params.title = searchTerm.trim();
+ params.keyword = searchTerm.trim();
}
if (category) {
params.category = category;
@@ -69,6 +78,7 @@
return params;
};
+
const fetchSeeds = async () => {
if (activeTab === '猜你喜欢') return;
setLoading(true);
@@ -121,9 +131,30 @@
try {
const response = await axios.get(`/seeds/${seedId}/download`, {
params: { passkey: user.userId },
- responseType: 'blob'
+ responseType: 'blob',
+ validateStatus: () => true // 允许处理非 2xx 响应
});
+ if (response.data && response.data.type === 'application/json') {
+ // 服务端返回的是 JSON 而不是 torrent 文件,尝试解析内容
+ const reader = new FileReader();
+ reader.onload = () => {
+ try {
+ const json = JSON.parse(reader.result);
+ if (json.code === 403 && json.msg === '您没有权限') {
+ toast.error('您已被封禁,如有疑问请联系管理员');
+ } else {
+ toast.error(json.msg || '下载失败,请稍后再试。');
+ }
+ } catch {
+ toast.error('下载失败,请稍后再试。');
+ }
+ };
+ reader.readAsText(response.data);
+ return;
+ }
+
+ // 如果是 torrent 文件
const blob = new Blob([response.data], { type: 'application/x-bittorrent' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
@@ -137,6 +168,7 @@
}
};
+
const handleFilterChange = (key, value) => {
setSelectedFilters(prev => ({ ...prev, [key]: value }));
};
@@ -161,16 +193,15 @@
<option value="最新">最新</option>
<option value="最热">最热</option>
</select>
- <select value={tagMode} onChange={(e) => setTagMode(e.target.value)} className="tag-mode-select">
+ {/* <select value={tagMode} onChange={(e) => setTagMode(e.target.value)} className="tag-mode-select">
<option value="any">包含任意标签</option>
<option value="all">包含所有标签</option>
- </select>
+ </select> */}
</div>
<div className="tag-filters">
{TAGS.map(tag => (
- <button
- // roles={["test"]}
+ <button
key={tag}
className={`tag-button ${activeTab === tag ? 'active-tag' : ''}`}
onClick={() => {
@@ -240,7 +271,8 @@
}
return (
- <Link to={`/seed/${seed.id}`} key={index} className="seed-item-link">
+ // <Link to={`/seed/${seed.id}`} key={index} className="seed-item-link">
+ <Link to={`/seed/${seed.id}`} key={seed.id} className="seed-item-link">
<div className="seed-item">
{seed.imageUrl && (
<img src={seed.imageUrl} alt={seed.title} className="seed-item-cover" />
@@ -255,12 +287,34 @@
</div>
</div>
</div>
- <div className="seed-item-size">{seed.size || '未知'}</div>
- <div className="seed-item-upload-time">{seed.upload_time?.split('T')[0] || '未知'}</div>
+ {/* <div className="seed-item-size">{seed.size || '未知'}</div> */}
+ <div className="seed-item-size">{seed.size ? formatBytes(seed.size) : '未知'}</div>
+
+ <div className="seed-item-upload-time">
+ {
+ (() => {
+ if (!seed.createdTime || !Array.isArray(seed.createdTime)) return '未知';
+ const [year, month, day, hour, minute, second] = seed.createdTime;
+ if ([year, month, day, hour, minute, second].some(v => typeof v !== 'number')) return '未知';
+
+ const date = new Date(year, month - 1, day, hour, minute, second);
+ if (isNaN(date.getTime())) return '未知';
+
+ // 格式化为 yyyy-mm-dd
+ const yyyy = date.getFullYear();
+ const mm = String(date.getMonth() + 1).padStart(2, '0'); // 月份要加1,补0
+ const dd = String(date.getDate()).padStart(2, '0');
+
+ return `${yyyy}-${mm}-${dd}`;
+ })()
+ }
+ </div>
+
+
<div className="seed-item-downloads">{seed.leechers ?? 0} 次下载</div>
<div className="seed-item-actions" onClick={e => e.stopPropagation()}>
<AuthButton
- roles={["cookie", "chocolate", "ice-cream"]}
+ roles={["cookie", "chocolate", "ice-cream"]}
className="btn-primary"
onClick={e => {
e.preventDefault();