blob: c4e0ec7c1cfb9fd33c790bdcc932e813f16e1ed1 [file] [log] [blame]
2081595154f846f912025-06-04 17:35:21 +08001<template>
2 <div class="home-page">
3 <div class="home-container">
xingjinwend652cc62025-06-04 19:52:19 +08004 <!-- 用户信息和导航 -->
5 <header class="site-header">
6 <div class="header-content">
7 <div class="logo-section">
8 <h2 class="site-logo">🚀 PT Tracker</h2>
2081595154f846f912025-06-04 17:35:21 +08009 </div>
xingjinwend652cc62025-06-04 19:52:19 +080010 <div class="user-section">
11 <div class="user-stats">
12 <div class="stat-item">
13 <span class="stat-label">上传:</span>
14 <span class="stat-value">{{ userStats.uploaded }}</span>
15 </div>
16 <div class="stat-item">
17 <span class="stat-label">下载:</span>
18 <span class="stat-value">{{ userStats.downloaded }}</span>
19 </div>
20 <div class="stat-item">
21 <span class="stat-label">分享率:</span>
22 <span class="stat-value ratio" :class="getRatioClass(userStats.ratio)">
23 {{ userStats.ratio }}
24 </span>
25 </div>
2081595154f846f912025-06-04 17:35:21 +080026 </div>
xingjinwend652cc62025-06-04 19:52:19 +080027 <div class="user-info">
28 <el-avatar :size="40" :icon="UserFilled" />
29 <span class="username">{{ userInfo.username }}</span>
30 <el-dropdown @command="handleUserCommand">
31 <el-icon class="dropdown-icon"><ArrowDown /></el-icon>
32 <template #dropdown>
33 <el-dropdown-menu>
34 <el-dropdown-item command="profile">个人资料</el-dropdown-item>
35 <el-dropdown-item command="settings">设置</el-dropdown-item>
36 <el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
37 </el-dropdown-menu>
38 </template>
39 </el-dropdown>
2081595154f846f912025-06-04 17:35:21 +080040 </div>
2081595154f846f912025-06-04 17:35:21 +080041 </div>
42 </div>
xingjinwend652cc62025-06-04 19:52:19 +080043 </header>
44
45 <!-- 主要内容区域 -->
46 <main class="main-content">
47 <!-- 统计概览 -->
48 <section class="stats-overview">
49 <div class="stats-grid">
50 <div class="stat-card">
51 <div class="stat-icon">
52 <el-icon size="32" color="#67c23a"><Download /></el-icon>
53 </div>
54 <div class="stat-info">
55 <h3>{{ siteStats.totalTorrents }}</h3>
56 <p>种子总数</p>
57 </div>
58 </div>
59 <div class="stat-card">
60 <div class="stat-icon">
61 <el-icon size="32" color="#409eff"><User /></el-icon>
62 </div>
63 <div class="stat-info">
64 <h3>{{ siteStats.totalUsers }}</h3>
65 <p>注册用户</p>
66 </div>
67 </div>
68 <div class="stat-card">
69 <div class="stat-icon">
70 <el-icon size="32" color="#f56c6c"><Upload /></el-icon>
71 </div>
72 <div class="stat-info">
73 <h3>{{ siteStats.totalSize }}</h3>
74 <p>数据总量</p>
75 </div>
76 </div>
77 </div>
78 </section>
79
80 <!-- 快速操作 -->
81 <section class="quick-actions">
82 <h2 class="section-title">快速操作</h2>
83 <div class="action-grid">
84 <div class="action-card" @click="navigateTo('/torrents')">
85 <el-icon size="48" color="#409eff"><Search /></el-icon>
86 <h3>浏览种子</h3>
87 <p>搜索和浏览所有可用的种子资源</p>
88 </div>
89 <div class="action-card" @click="navigateTo('/upload')">
90 <el-icon size="48" color="#67c23a"><Upload /></el-icon>
91 <h3>上传资源</h3>
92 <p>分享你的资源,为社区做贡献</p>
93 </div>
94 <div class="action-card" @click="navigateTo('/forum')">
95 <el-icon size="48" color="#e6a23c"><ChatDotRound /></el-icon>
96 <h3>论坛交流</h3>
97 <p>与其他用户交流讨论</p>
98 </div>
99 <div class="action-card" @click="navigateTo('/rankings')">
100 <el-icon size="48" color="#f56c6c"><TrophyBase /></el-icon>
101 <h3>排行榜</h3>
102 <p>查看用户和资源排行</p>
103 </div>
104 </div>
105 </section>
106
107 <!-- 最新种子 -->
108 <section class="latest-torrents">
109 <div class="section-header">
110 <h2 class="section-title">最新种子</h2>
111 <el-button type="primary" text @click="navigateTo('/torrents')">
112 查看更多 <el-icon><ArrowRight /></el-icon>
113 </el-button>
114 </div>
115 <div class="torrents-list">
116 <div
117 v-for="torrent in latestTorrents"
118 :key="torrent.id"
119 class="torrent-item"
120 @click="navigateTo(`/torrent/${torrent.id}`)"
121 >
122 <div class="torrent-info">
123 <div class="torrent-category">
124 <el-tag :type="getCategoryType(torrent.category)" size="small">
125 {{ torrent.category }}
126 </el-tag>
127 </div>
128 <h4 class="torrent-title">{{ torrent.title }}</h4>
129 <div class="torrent-meta">
130 <span class="meta-item">
131 <el-icon><User /></el-icon>
132 {{ torrent.uploader }}
133 </span>
134 <span class="meta-item">
135 <el-icon><Clock /></el-icon>
136 {{ formatTime(torrent.uploadTime) }}
137 </span>
138 <span class="meta-item">
139 <el-icon><Coin /></el-icon>
140 {{ torrent.size }}
141 </span>
142 </div>
143 </div>
144 <div class="torrent-stats">
145 <div class="stat-group">
146 <span class="stat-number seeders">{{ torrent.seeders }}</span>
147 <span class="stat-label">做种</span>
148 </div>
149 <div class="stat-group">
150 <span class="stat-number leechers">{{ torrent.leechers }}</span>
151 <span class="stat-label">下载</span>
152 </div>
153 <div class="stat-group">
154 <span class="stat-number">{{ torrent.downloads }}</span>
155 <span class="stat-label">完成</span>
156 </div>
157 </div>
158 </div>
159 </div>
160 </section>
161
162 <!-- 个人活动 -->
163 <section class="user-activity">
164 <h2 class="section-title">我的活动</h2>
165 <div class="activity-grid">
166 <div class="activity-card">
167 <h3>我的种子</h3>
168 <div class="activity-stats">
169 <div class="activity-item">
170 <span class="activity-label">正在做种:</span>
171 <span class="activity-value">{{ userActivity.seeding }}</span>
172 </div>
173 <div class="activity-item">
174 <span class="activity-label">正在下载:</span>
175 <span class="activity-value">{{ userActivity.downloading }}</span>
176 </div>
177 <div class="activity-item">
178 <span class="activity-label">已上传:</span>
179 <span class="activity-value">{{ userActivity.uploaded }}</span>
180 </div>
181 </div>
182 <el-button type="primary" size="small" @click="navigateTo('/my-torrents')">
183 查看详情
184 </el-button>
185 </div>
186
187 <div class="activity-card">
188 <h3>邀请管理</h3>
189 <div class="activity-stats">
190 <div class="activity-item">
191 <span class="activity-label">可用邀请:</span>
192 <span class="activity-value">{{ userActivity.invitations }}</span>
193 </div>
194 <div class="activity-item">
195 <span class="activity-label">已邀请:</span>
196 <span class="activity-value">{{ userActivity.invited }}</span>
197 </div>
198 </div>
199 <el-button type="success" size="small" @click="navigateTo('/invitations')">
200 管理邀请
201 </el-button>
202 </div>
203 </div>
204 </section>
205 </main>
2081595154f846f912025-06-04 17:35:21 +0800206 </div>
207 </div>
208</template>
209
210<script>
211import { ref, onMounted } from 'vue'
212import { useRouter } from 'vue-router'
213import { ElMessage, ElMessageBox } from 'element-plus'
214import {
215 UserFilled,
xingjinwend652cc62025-06-04 19:52:19 +0800216 ArrowDown,
2081595154f846f912025-06-04 17:35:21 +0800217 Download,
218 Upload,
219 ChatDotRound,
220 Search,
xingjinwend652cc62025-06-04 19:52:19 +0800221 User,
222 Connection,
223 TrophyBase,
224 ArrowRight,
225 Clock,
226 Coin
2081595154f846f912025-06-04 17:35:21 +0800227} from '@element-plus/icons-vue'
228
229export default {
230 name: 'HomeView',
231 components: {
xingjinwend652cc62025-06-04 19:52:19 +0800232 UserFilled,
233 ArrowDown
2081595154f846f912025-06-04 17:35:21 +0800234 },
235 setup() {
236 const router = useRouter()
xingjinwend652cc62025-06-04 19:52:19 +0800237
2081595154f846f912025-06-04 17:35:21 +0800238 const userInfo = ref({
239 username: '',
240 loginTime: ''
241 })
242
xingjinwend652cc62025-06-04 19:52:19 +0800243 const userStats = ref({
244 uploaded: '128.5 GB',
245 downloaded: '45.2 GB',
246 ratio: '2.84'
247 })
248
249 const siteStats = ref({
250 totalTorrents: '12,458',
251 totalUsers: '8,924',
252 onlineUsers: '342',
253 totalSize: '45.2 TB'
254 })
255
256 const userActivity = ref({
257 seeding: 15,
258 downloading: 3,
259 uploaded: 8,
260 invitations: 5,
261 invited: 12
262 })
263
264 const latestTorrents = ref([
265 {
266 id: 1,
267 title: '[4K蓝光原盘] 阿凡达:水之道 Avatar: The Way of Water (2022)',
268 category: '电影',
269 uploader: 'MovieMaster',
270 uploadTime: '2025-06-03T10:30:00',
271 size: '85.6 GB',
272 seeders: 128,
273 leechers: 45,
274 downloads: 892
275 },
276 {
277 id: 2,
278 title: '[FLAC] Taylor Swift - Midnights (Deluxe Edition) [2022]',
279 category: '音乐',
280 uploader: 'MusicLover',
281 uploadTime: '2025-06-03T09:15:00',
282 size: '1.2 GB',
283 seeders: 67,
284 leechers: 12,
285 downloads: 456
286 },
287 {
288 id: 3,
289 title: '[合集] Adobe Creative Suite 2025 完整版',
290 category: '软件',
291 uploader: 'TechGuru',
292 uploadTime: '2025-06-03T08:45:00',
293 size: '12.8 GB',
294 seeders: 234,
295 leechers: 89,
296 downloads: 1205
297 }
298 ])
299
2081595154f846f912025-06-04 17:35:21 +0800300 onMounted(() => {
301 userInfo.value = {
302 username: localStorage.getItem('username') || '用户',
303 loginTime: localStorage.getItem('loginTime') || ''
304 }
xingjinwend652cc62025-06-04 19:52:19 +0800305
306 // 模拟获取用户数据
307 fetchUserData()
2081595154f846f912025-06-04 17:35:21 +0800308 })
309
xingjinwend652cc62025-06-04 19:52:19 +0800310 const fetchUserData = () => {
311 // 这里应该是API调用,现在使用模拟数据
312 console.log('获取用户数据...')
313 }
314
2081595154f846f912025-06-04 17:35:21 +0800315 const formatTime = (timeString) => {
316 if (!timeString) return ''
317 const date = new Date(timeString)
xingjinwend652cc62025-06-04 19:52:19 +0800318 const now = new Date()
319 const diff = now - date
320 const hours = Math.floor(diff / (1000 * 60 * 60))
321
322 if (hours < 1) return '刚刚'
323 if (hours < 24) return `${hours}小时前`
324 const days = Math.floor(hours / 24)
325 return `${days}天前`
2081595154f846f912025-06-04 17:35:21 +0800326 }
327
xingjinwend652cc62025-06-04 19:52:19 +0800328 const getRatioClass = (ratio) => {
329 const r = parseFloat(ratio)
330 if (r >= 2) return 'excellent'
331 if (r >= 1) return 'good'
332 return 'warning'
333 }
334
335 const getCategoryType = (category) => {
336 const types = {
337 '电影': 'primary',
338 '音乐': 'success',
339 '软件': 'warning',
340 '游戏': 'danger',
341 '电视剧': 'info'
342 }
343 return types[category] || 'default'
344 }
345
346 const navigateTo = (path) => {
347 router.push(path)
348 }
349
350 const handleUserCommand = async (command) => {
351 switch (command) {
352 case 'profile':
353 navigateTo('/profile')
354 break
355 case 'settings':
356 navigateTo('/settings')
357 break
358 case 'logout':
359 await handleLogout()
360 break
361 }
362 }
363
2081595154f846f912025-06-04 17:35:21 +0800364 const handleLogout = async () => {
365 try {
366 await ElMessageBox.confirm(
367 '确定要退出登录吗?',
368 '提示',
369 {
370 confirmButtonText: '确定',
371 cancelButtonText: '取消',
372 type: 'warning'
373 }
374 )
375
2081595154f846f912025-06-04 17:35:21 +0800376 localStorage.removeItem('isLoggedIn')
377 localStorage.removeItem('username')
378 localStorage.removeItem('loginTime')
379 localStorage.removeItem('rememberLogin')
380
381 ElMessage.success('已安全退出')
382 router.push('/login')
383
384 } catch {
385 // 用户取消退出
386 }
387 }
388
389 return {
390 userInfo,
xingjinwend652cc62025-06-04 19:52:19 +0800391 userStats,
392 siteStats,
393 userActivity,
394 latestTorrents,
2081595154f846f912025-06-04 17:35:21 +0800395 formatTime,
xingjinwend652cc62025-06-04 19:52:19 +0800396 getRatioClass,
397 getCategoryType,
398 navigateTo,
399 handleUserCommand,
2081595154f846f912025-06-04 17:35:21 +0800400 handleLogout,
2081595154f846f912025-06-04 17:35:21 +0800401 Download,
402 Upload,
xingjinwend652cc62025-06-04 19:52:19 +0800403 ChatDotRound,
404 Search,
405 User,
406 Connection,
407 TrophyBase,
408 ArrowRight,
409 Clock,
410 Coin
2081595154f846f912025-06-04 17:35:21 +0800411 }
412 }
413}
414</script>
415
416<style lang="scss" scoped>
417.home-page {
418 min-height: 100vh;
419 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
xingjinwend652cc62025-06-04 19:52:19 +0800420 padding: 0;
2081595154f846f912025-06-04 17:35:21 +0800421}
422
423.home-container {
xingjinwend652cc62025-06-04 19:52:19 +0800424 max-width: 1200px;
425 margin: 0 auto;
426 background: #fff;
427 min-height: 100vh;
2081595154f846f912025-06-04 17:35:21 +0800428}
429
xingjinwend652cc62025-06-04 19:52:19 +0800430// 网站头部
431.site-header {
432 background: #fff;
433 box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
434 position: sticky;
435 top: 0;
436 z-index: 100;
437
438 .header-content {
439 display: flex;
440 justify-content: space-between;
441 align-items: center;
442 padding: 16px 24px;
2081595154f846f912025-06-04 17:35:21 +0800443
xingjinwend652cc62025-06-04 19:52:19 +0800444 .logo-section {
445 .site-logo {
2081595154f846f912025-06-04 17:35:21 +0800446 margin: 0;
xingjinwend652cc62025-06-04 19:52:19 +0800447 font-size: 24px;
448 font-weight: bold;
449 color: #667eea;
2081595154f846f912025-06-04 17:35:21 +0800450 }
451 }
452
xingjinwend652cc62025-06-04 19:52:19 +0800453 .user-section {
454 display: flex;
455 align-items: center;
456 gap: 24px;
2081595154f846f912025-06-04 17:35:21 +0800457
xingjinwend652cc62025-06-04 19:52:19 +0800458 .user-stats {
459 display: flex;
460 gap: 16px;
2081595154f846f912025-06-04 17:35:21 +0800461
xingjinwend652cc62025-06-04 19:52:19 +0800462 .stat-item {
463 font-size: 14px;
464
465 .stat-label {
466 color: #909399;
467 margin-right: 4px;
468 }
469
470 .stat-value {
471 font-weight: 600;
472 color: #2c3e50;
473
474 &.ratio {
475 &.excellent { color: #67c23a; }
476 &.good { color: #e6a23c; }
477 &.warning { color: #f56c6c; }
478 }
479 }
480 }
481 }
482
483 .user-info {
484 display: flex;
485 align-items: center;
486 gap: 8px;
487 cursor: pointer;
488
489 .username {
490 font-weight: 500;
491 color: #2c3e50;
2081595154f846f912025-06-04 17:35:21 +0800492 }
493
xingjinwend652cc62025-06-04 19:52:19 +0800494 .dropdown-icon {
495 color: #909399;
496 transition: transform 0.3s ease;
497
498 &:hover {
499 transform: rotate(180deg);
500 }
501 }
502 }
503 }
504 }
505}
506
507// 主要内容
508.main-content {
509 padding: 24px;
510}
511
512.section-title {
513 font-size: 20px;
514 font-weight: 600;
515 color: #2c3e50;
516 margin-bottom: 16px;
517}
518
519.section-header {
520 display: flex;
521 justify-content: space-between;
522 align-items: center;
523 margin-bottom: 16px;
524}
525
526// 统计概览
527.stats-overview {
528 margin-bottom: 32px;
529
530 .stats-grid {
531 display: grid;
532 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
533 gap: 16px;
534
535 .stat-card {
536 background: #fff;
537 border-radius: 12px;
538 padding: 24px;
539 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
540 border: 1px solid #f0f0f0;
541 display: flex;
542 align-items: center;
543 gap: 16px;
544 transition: all 0.3s ease;
545
546 &:hover {
547 transform: translateY(-2px);
548 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
549 }
550
551 .stat-info {
2081595154f846f912025-06-04 17:35:21 +0800552 h3 {
xingjinwend652cc62025-06-04 19:52:19 +0800553 font-size: 24px;
554 font-weight: bold;
2081595154f846f912025-06-04 17:35:21 +0800555 color: #2c3e50;
xingjinwend652cc62025-06-04 19:52:19 +0800556 margin: 0 0 4px 0;
2081595154f846f912025-06-04 17:35:21 +0800557 }
558
559 p {
560 font-size: 14px;
xingjinwend652cc62025-06-04 19:52:19 +0800561 color: #909399;
2081595154f846f912025-06-04 17:35:21 +0800562 margin: 0;
2081595154f846f912025-06-04 17:35:21 +0800563 }
564 }
565 }
xingjinwend652cc62025-06-04 19:52:19 +0800566 }
567}
568
569// 快速操作
570.quick-actions {
571 margin-bottom: 32px;
572
573 .action-grid {
574 display: grid;
575 grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
576 gap: 20px;
2081595154f846f912025-06-04 17:35:21 +0800577
xingjinwend652cc62025-06-04 19:52:19 +0800578 .action-card {
579 background: #fff;
580 border-radius: 12px;
581 padding: 32px 24px;
582 text-align: center;
583 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
584 border: 1px solid #f0f0f0;
585 cursor: pointer;
586 transition: all 0.3s ease;
2081595154f846f912025-06-04 17:35:21 +0800587
xingjinwend652cc62025-06-04 19:52:19 +0800588 &:hover {
589 transform: translateY(-4px);
590 box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
591 }
592
593 h3 {
594 font-size: 18px;
595 font-weight: 600;
596 color: #2c3e50;
597 margin: 16px 0 8px 0;
598 }
599
600 p {
601 font-size: 14px;
602 color: #7f8c8d;
603 line-height: 1.5;
604 margin: 0;
605 }
606 }
607 }
608}
609
610// 最新种子
611.latest-torrents {
612 margin-bottom: 32px;
613
614 .torrents-list {
615 background: #fff;
616 border-radius: 12px;
617 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
618 border: 1px solid #f0f0f0;
619 overflow: hidden;
620
621 .torrent-item {
622 display: flex;
623 justify-content: space-between;
624 align-items: center;
625 padding: 20px 24px;
626 border-bottom: 1px solid #f5f5f5;
627 cursor: pointer;
628 transition: background-color 0.3s ease;
629
630 &:hover {
631 background-color: #f8f9fa;
632 }
633
634 &:last-child {
635 border-bottom: none;
636 }
637
638 .torrent-info {
639 flex: 1;
2081595154f846f912025-06-04 17:35:21 +0800640
xingjinwend652cc62025-06-04 19:52:19 +0800641 .torrent-category {
642 margin-bottom: 8px;
643 }
644
645 .torrent-title {
646 font-size: 16px;
647 font-weight: 500;
648 color: #2c3e50;
649 margin: 0 0 8px 0;
650 line-height: 1.4;
651 }
652
653 .torrent-meta {
654 display: flex;
655 gap: 16px;
656
657 .meta-item {
658 display: flex;
659 align-items: center;
660 gap: 4px;
661 font-size: 12px;
662 color: #909399;
663
664 .el-icon {
665 font-size: 12px;
666 }
667 }
668 }
669 }
670
671 .torrent-stats {
672 display: flex;
673 gap: 24px;
674
675 .stat-group {
676 text-align: center;
677
678 .stat-number {
679 display: block;
680 font-size: 16px;
681 font-weight: 600;
682
683 &.seeders { color: #67c23a; }
684 &.leechers { color: #f56c6c; }
685 }
686
687 .stat-label {
688 font-size: 12px;
689 color: #909399;
690 }
691 }
692 }
693 }
694 }
695}
696
697// 用户活动
698.user-activity {
699 .activity-grid {
700 display: grid;
701 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
702 gap: 20px;
703
704 .activity-card {
705 background: #fff;
706 border-radius: 12px;
707 padding: 24px;
708 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
709 border: 1px solid #f0f0f0;
710
711 h3 {
712 font-size: 18px;
713 font-weight: 600;
714 color: #2c3e50;
715 margin: 0 0 16px 0;
716 }
717
718 .activity-stats {
719 margin-bottom: 16px;
720
721 .activity-item {
722 display: flex;
723 justify-content: space-between;
724 align-items: center;
725 padding: 8px 0;
726
727 .activity-label {
728 font-size: 14px;
729 color: #7f8c8d;
730 }
731
732 .activity-value {
733 font-size: 14px;
734 font-weight: 600;
735 color: #2c3e50;
736 }
2081595154f846f912025-06-04 17:35:21 +0800737 }
738 }
739 }
740 }
741}
742
743// 响应式设计
744@media (max-width: 768px) {
xingjinwend652cc62025-06-04 19:52:19 +0800745 .header-content {
746 flex-direction: column;
747 gap: 16px;
748
749 .user-stats {
750 flex-direction: column;
751 gap: 8px;
752 text-align: center;
753 }
754 }
755
756 .main-content {
2081595154f846f912025-06-04 17:35:21 +0800757 padding: 16px;
758 }
759
xingjinwend652cc62025-06-04 19:52:19 +0800760 .stats-grid,
761 .action-grid,
762 .activity-grid {
763 grid-template-columns: 1fr;
764 }
765
766 .torrent-item {
767 flex-direction: column;
768 align-items: flex-start;
769 gap: 16px;
2081595154f846f912025-06-04 17:35:21 +0800770
xingjinwend652cc62025-06-04 19:52:19 +0800771 .torrent-stats {
772 width: 100%;
773 justify-content: space-around;
2081595154f846f912025-06-04 17:35:21 +0800774 }
775 }
776}
777</style>