blob: e02971e5f87b1d701dd50942c6a5538695874db7 [file] [log] [blame]
vulgar520152afbcf2025-06-07 02:34:46 +08001<template>
2 <div class="torrents-page">
3 <div class="page-header">
4 <h1>种子资源</h1>
5 <div class="header-actions">
6 <el-button type="primary" :icon="Upload" @click="$router.push('/upload')">
7 上传种子
8 </el-button>
9 </div>
10 </div>
11
12 <!-- 搜索和筛选 -->
13 <div class="search-section">
14 <div class="search-bar">
15 <el-input
16 v-model="searchQuery"
17 placeholder="搜索种子..."
18 :prefix-icon="Search"
19 size="large"
20 @keyup.enter="handleSearch"
21 clearable
22 />
23 <el-button type="primary" size="large" @click="handleSearch">
24 搜索
25 </el-button>
26 </div>
27
28 <div class="filters">
29 <el-select v-model="selectedCategory" placeholder="分类" @change="handleFilter">
30 <el-option label="全部" value="" />
31 <el-option label="电影" value="movie" />
32 <el-option label="电视剧" value="tv" />
33 <el-option label="音乐" value="music" />
34 <el-option label="软件" value="software" />
35 <el-option label="游戏" value="game" />
36 </el-select>
37
38
39 </div>
40 </div>
41
42 <!-- 种子列表 -->
43 <div class="torrents-list">
44 <div class="list-header">
45 <span class="results-count">共找到 {{ totalCount }} 个种子</span>
46 </div>
47
48 <!-- <div style="background: yellow; padding: 10px; margin: 10px 0;">
49 <p>调试信息:</p>
50 <p>torrents长度: {{ torrents.length }}</p>
51 <p>totalCount: {{ totalCount }}</p>
52 <p>loading: {{ loading }}</p>
53 <pre>{{ JSON.stringify(torrents[0], null, 2) }}</pre>
54 </div> -->
55
56 <el-table
57 :data="torrents"
58 v-loading="loading"
59 @row-click="handleRowClick"
60 stripe
61 class="torrents-table"
62 >
63 <el-table-column label="分类" width="100">
64 <template #default="{ row }">
65 <el-tag
66 :type="getCategoryType(row.category)"
67 size="small"
68 >
69 {{ row.category?.name || '未分类' }}
70 </el-tag>
71 </template>
72 </el-table-column>
73
74 <el-table-column label="种子信息" min-width="400">
75 <template #default="{ row }">
76 <div class="torrent-info">
77 <h4 class="torrent-title">{{ row.title }}</h4>
78 <div class="torrent-subtitle" v-if="row.subTitle">
79 {{ row.subTitle }}
80 </div>
81 <div class="torrent-meta">
82 <span class="uploader">
83 <el-icon><User /></el-icon>
84 {{ row.anonymous ? '匿名用户' : row.user?.username }}
85 </span>
86 <span class="upload-time">
87 <el-icon><Clock /></el-icon>
88 {{ formatTime(row.createdAt) }}
89 </span>
90 <span class="file-size">
91 <el-icon><Document /></el-icon>
92 {{ formatSize(row.size) }}
93 </span>
94 </div>
95 <div class="torrent-tags" v-if="row.tag && row.tag.length > 0">
96 <el-tag
97 v-for="tag in row.tag"
98 :key="tag"
99 size="small"
100 type="info"
101 class="tag-item"
102 >
103 {{ tag }}
104 </el-tag>
105 </div>
106 </div>
107 </template>
108 </el-table-column>
109
110 <el-table-column label="做种" width="80" align="center">
111 <template #default="{ row }">
112 <span class="seeders">{{ row.seeders || 0 }}</span>
113 </template>
114 </el-table-column>
115
116 <el-table-column label="下载" width="80" align="center">
117 <template #default="{ row }">
118 <span class="leechers">{{ row.leechers || 0 }}</span>
119 </template>
120 </el-table-column>
121
122 <el-table-column label="完成" width="80" align="center">
123 <template #default="{ row }">
124 <span>{{ row.downloads || 0 }}</span>
125 </template>
126 </el-table-column>
127
128 <el-table-column label="操作" width="120" align="center">
129 <template #default="{ row }">
130 <el-button
131 type="primary"
132 size="small"
133 :icon="Download"
134 @click.stop="handleDownload(row)"
135 >
136 下载
137 </el-button>
138 </template>
139 </el-table-column>
140 </el-table>
141
142 <!-- 分页 -->
143 <div class="pagination-wrapper">
144 <el-pagination
145 v-model:current-page="currentPage"
146 v-model:page-size="pageSize"
147 :page-sizes="[10,20, 50, 100]"
148 :total="totalCount"
149 layout="total, sizes, prev, pager, next, jumper"
150 @size-change="handleSizeChange"
151 @current-change="handleCurrentChange"
152 />
153 </div>
154 </div>
155 </div>
156</template>
157
158<script>
159import { ref, onMounted, watch } from 'vue'
160import { useRouter, useRoute } from 'vue-router'
161import { ElMessage } from 'element-plus'
162import {
163 Search,
164 Upload,
165 Download,
166 User,
167 Clock,
168 Document
169} from '@element-plus/icons-vue'
170import { searchTorrents } from '@/api/torrent'
171
172export default {
173 name: 'TorrentsView',
174 setup() {
175 const router = useRouter()
176 const route = useRoute()
177
178 const loading = ref(false)
179 const searchQuery = ref('')
180 const selectedCategory = ref('')
181 const sortBy = ref('upload_time')
182 const sortOrder = ref('desc')
183 const currentPage = ref(1)
184 const pageSize = ref(20)
185 const totalCount = ref(0)
186 const totalPages = ref(0)
187
188 const torrents = ref([])
189
190 onMounted(() => {
191 // 从URL参数初始化搜索条件
192 if (route.query.q) {
193 searchQuery.value = route.query.q
194 }
195 if (route.query.category) {
196 selectedCategory.value = route.query.category
197 }
198 if (route.query.page) {
199 currentPage.value = parseInt(route.query.page)
200 }
201
202 fetchTorrents()
203 })
204
205 const fetchTorrents = async () => {
206 loading.value = true
207 try {
208 const searchParams = {
209 keyword: searchQuery.value || '', // 搜索关键词
210 page: currentPage.value - 1, // 后端页码从0开始
211 entriesPerPage: pageSize.value // 每页显示数量
212 }
213
214 console.log('🔍 发送搜索请求,参数:', searchParams)
215
216 const response = await searchTorrents(searchParams)
217
218 console.log('✅ 接收到响应:', response)
219
220 if (response) { // response直接就是数据
221 torrents.value = response.torrents || []
222 totalCount.value = response.totalElements || 0
223 totalPages.value = response.totalPages || 1
224
225 console.log('📊 处理后的数据:', {
226 torrentsCount: torrents.value.length,
227 totalCount: totalCount.value,
228 firstTorrent: torrents.value[0]
229 })
230 }
231 } catch (error) {
232 console.error('获取种子列表失败:', error)
233 console.error('📝 错误详情:', {
234 message: error.message,
235 status: error.response?.status,
236 data: error.response?.data
237 })
238 ElMessage.error(`获取种子列表失败: ${error.response?.data?.message || error.message}`)
239 // 如果请求失败,清空数据
240 torrents.value = []
241 totalCount.value = 0
242 } finally {
243 loading.value = false
244 }
245 }
246
247 const handleSearch = () => {
248 currentPage.value = 1
249 updateURL()
250 fetchTorrents()
251 }
252
253 const handleFilter = () => {
254 currentPage.value = 1
255 updateURL()
256 fetchTorrents()
257 }
258
259 const updateURL = () => {
260 const query = {}
261 if (searchQuery.value) query.q = searchQuery.value
262 if (selectedCategory.value) query.category = selectedCategory.value
263 if (currentPage.value > 1) query.page = currentPage.value
264
265 router.replace({ query })
266 }
267
268 const handleRowClick = (row) => {
269 router.push(`/torrent/${row.id || row.infoHash}`)
270 }
271
272 const handleDownload = (row) => {
273 ElMessage.success(`开始下载: ${row.title || row.name}`)
274 // 这里实现下载逻辑
275 }
276
277 const handleSizeChange = (size) => {
278 pageSize.value = size
279 currentPage.value = 1
280 fetchTorrents()
281 }
282
283 const handleCurrentChange = (page) => {
284 currentPage.value = page
285 updateURL()
286 fetchTorrents()
287 }
288
289 const formatTime = (timeString) => {
290 if (!timeString) return '-'
291
292 const date = new Date(timeString)
293 const now = new Date()
294 const diff = now - date
295 const hours = Math.floor(diff / (1000 * 60 * 60))
296
297 if (hours < 1) return '刚刚'
298 if (hours < 24) return `${hours}小时前`
299 const days = Math.floor(hours / 24)
300 if (days < 30) return `${days}天前`
301
302 return date.toLocaleDateString('zh-CN')
303 }
304
305 const formatSize = (sizeInBytes) => {
306 if (!sizeInBytes) return '-'
307
308 const units = ['B', 'KB', 'MB', 'GB', 'TB']
309 let size = sizeInBytes
310 let unitIndex = 0
311
312 while (size >= 1024 && unitIndex < units.length - 1) {
313 size /= 1024
314 unitIndex++
315 }
316
317 return `${size.toFixed(1)} ${units[unitIndex]}`
318 }
319
320 const getCategoryType = (category) => {
321 if (!category) return 'default'
322 const types = {
323 'os': 'primary',
324 'movie': 'success',
325 'tv': 'info',
326 'music': 'warning',
327 'software': 'danger'
328 }
329 return types[category.slug] || 'default'
330 }
331
332 return {
333 loading,
334 searchQuery,
335 selectedCategory,
336 sortBy,
337 sortOrder,
338 currentPage,
339 pageSize,
340 totalCount,
341 torrents,
342 handleSearch,
343 handleFilter,
344 handleRowClick,
345 handleDownload,
346 handleSizeChange,
347 handleCurrentChange,
348 formatTime,
349 formatSize,
350 getCategoryType,
351 Search,
352 Upload,
353 Download,
354 User,
355 Clock,
356 Document
357 }
358 }
359}
360</script>
361
362<style lang="scss" scoped>
363.torrents-page {
364 max-width: 1200px;
365 margin: 0 auto;
366 padding: 24px;
367}
368
369.page-header {
370 display: flex;
371 justify-content: space-between;
372 align-items: center;
373 margin-bottom: 24px;
374
375 h1 {
376 font-size: 28px;
377 font-weight: 600;
378 color: #2c3e50;
379 margin: 0;
380 }
381}
382
383.search-section {
384 background: #fff;
385 border-radius: 12px;
386 padding: 24px;
387 margin-bottom: 24px;
388 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
389
390 .search-bar {
391 display: flex;
392 gap: 12px;
393 margin-bottom: 16px;
394
395 .el-input {
396 flex: 1;
397 }
398 }
399
400 .filters {
401 display: flex;
402 gap: 16px;
403 flex-wrap: wrap;
404 align-items: center;
405
406 .el-select {
407 width: 120px;
408 }
409 }
410}
411
412.torrents-list {
413 background: #fff;
414 border-radius: 12px;
415 padding: 24px;
416 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
417
418 .list-header {
419 margin-bottom: 16px;
420
421 .results-count {
422 font-size: 14px;
423 color: #909399;
424 }
425 }
426
427 .torrents-table {
428 .torrent-info {
429 .torrent-title {
430 font-size: 16px;
431 font-weight: 500;
432 color: #2c3e50;
433 margin: 0 0 8px 0;
434 line-height: 1.4;
435 cursor: pointer;
436
437 &:hover {
438 color: #409eff;
439 }
440 }
441
442 .torrent-meta {
443 display: flex;
444 gap: 16px;
445 font-size: 12px;
446 color: #909399;
447
448 span {
449 display: flex;
450 align-items: center;
451 gap: 4px;
452 }
453 }
454 }
455
456 .seeders {
457 color: #67c23a;
458 font-weight: 600;
459 }
460
461 .leechers {
462 color: #f56c6c;
463 font-weight: 600;
464 }
465 }
466
467 .pagination-wrapper {
468 margin-top: 24px;
469 text-align: center;
470 }
471}
472
473@media (max-width: 768px) {
474 .torrents-page {
475 padding: 16px;
476 }
477
478 .page-header {
479 flex-direction: column;
480 gap: 16px;
481 align-items: flex-start;
482 }
483
484 .filters {
485 flex-direction: column;
486 align-items: flex-start;
487
488 .el-select {
489 width: 100%;
490 }
491 }
492
493 .torrents-table {
494 :deep(.el-table__header),
495 :deep(.el-table__body) {
496 font-size: 12px;
497 }
498 }
499}
xingjinwend652cc62025-06-04 19:52:19 +0800500</style>