前后端登录注册连接成功
Change-Id: Ib5f9282fe7217b3363e542ce5c4e1c0d32619dcb
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index c4e0ec7..7a728b1 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -1,777 +1,665 @@
-<template>
- <div class="home-page">
- <div class="home-container">
- <!-- 用户信息和导航 -->
- <header class="site-header">
- <div class="header-content">
- <div class="logo-section">
- <h2 class="site-logo">🚀 PT Tracker</h2>
- </div>
- <div class="user-section">
- <div class="user-stats">
- <div class="stat-item">
- <span class="stat-label">上传:</span>
- <span class="stat-value">{{ userStats.uploaded }}</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">下载:</span>
- <span class="stat-value">{{ userStats.downloaded }}</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">分享率:</span>
- <span class="stat-value ratio" :class="getRatioClass(userStats.ratio)">
- {{ userStats.ratio }}
- </span>
- </div>
- </div>
- <div class="user-info">
- <el-avatar :size="40" :icon="UserFilled" />
- <span class="username">{{ userInfo.username }}</span>
- <el-dropdown @command="handleUserCommand">
- <el-icon class="dropdown-icon"><ArrowDown /></el-icon>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item command="profile">个人资料</el-dropdown-item>
- <el-dropdown-item command="settings">设置</el-dropdown-item>
- <el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
- </div>
- </div>
- </header>
-
- <!-- 主要内容区域 -->
- <main class="main-content">
- <!-- 统计概览 -->
- <section class="stats-overview">
- <div class="stats-grid">
- <div class="stat-card">
- <div class="stat-icon">
- <el-icon size="32" color="#67c23a"><Download /></el-icon>
- </div>
- <div class="stat-info">
- <h3>{{ siteStats.totalTorrents }}</h3>
- <p>种子总数</p>
- </div>
- </div>
- <div class="stat-card">
- <div class="stat-icon">
- <el-icon size="32" color="#409eff"><User /></el-icon>
- </div>
- <div class="stat-info">
- <h3>{{ siteStats.totalUsers }}</h3>
- <p>注册用户</p>
- </div>
- </div>
- <div class="stat-card">
- <div class="stat-icon">
- <el-icon size="32" color="#f56c6c"><Upload /></el-icon>
- </div>
- <div class="stat-info">
- <h3>{{ siteStats.totalSize }}</h3>
- <p>数据总量</p>
- </div>
- </div>
- </div>
- </section>
-
- <!-- 快速操作 -->
- <section class="quick-actions">
- <h2 class="section-title">快速操作</h2>
- <div class="action-grid">
- <div class="action-card" @click="navigateTo('/torrents')">
- <el-icon size="48" color="#409eff"><Search /></el-icon>
- <h3>浏览种子</h3>
- <p>搜索和浏览所有可用的种子资源</p>
- </div>
- <div class="action-card" @click="navigateTo('/upload')">
- <el-icon size="48" color="#67c23a"><Upload /></el-icon>
- <h3>上传资源</h3>
- <p>分享你的资源,为社区做贡献</p>
- </div>
- <div class="action-card" @click="navigateTo('/forum')">
- <el-icon size="48" color="#e6a23c"><ChatDotRound /></el-icon>
- <h3>论坛交流</h3>
- <p>与其他用户交流讨论</p>
- </div>
- <div class="action-card" @click="navigateTo('/rankings')">
- <el-icon size="48" color="#f56c6c"><TrophyBase /></el-icon>
- <h3>排行榜</h3>
- <p>查看用户和资源排行</p>
- </div>
- </div>
- </section>
-
- <!-- 最新种子 -->
- <section class="latest-torrents">
- <div class="section-header">
- <h2 class="section-title">最新种子</h2>
- <el-button type="primary" text @click="navigateTo('/torrents')">
- 查看更多 <el-icon><ArrowRight /></el-icon>
- </el-button>
- </div>
- <div class="torrents-list">
- <div
- v-for="torrent in latestTorrents"
- :key="torrent.id"
- class="torrent-item"
- @click="navigateTo(`/torrent/${torrent.id}`)"
- >
- <div class="torrent-info">
- <div class="torrent-category">
- <el-tag :type="getCategoryType(torrent.category)" size="small">
- {{ torrent.category }}
- </el-tag>
- </div>
- <h4 class="torrent-title">{{ torrent.title }}</h4>
- <div class="torrent-meta">
- <span class="meta-item">
- <el-icon><User /></el-icon>
- {{ torrent.uploader }}
- </span>
- <span class="meta-item">
- <el-icon><Clock /></el-icon>
- {{ formatTime(torrent.uploadTime) }}
- </span>
- <span class="meta-item">
- <el-icon><Coin /></el-icon>
- {{ torrent.size }}
- </span>
- </div>
- </div>
- <div class="torrent-stats">
- <div class="stat-group">
- <span class="stat-number seeders">{{ torrent.seeders }}</span>
- <span class="stat-label">做种</span>
- </div>
- <div class="stat-group">
- <span class="stat-number leechers">{{ torrent.leechers }}</span>
- <span class="stat-label">下载</span>
- </div>
- <div class="stat-group">
- <span class="stat-number">{{ torrent.downloads }}</span>
- <span class="stat-label">完成</span>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <!-- 个人活动 -->
- <section class="user-activity">
- <h2 class="section-title">我的活动</h2>
- <div class="activity-grid">
- <div class="activity-card">
- <h3>我的种子</h3>
- <div class="activity-stats">
- <div class="activity-item">
- <span class="activity-label">正在做种:</span>
- <span class="activity-value">{{ userActivity.seeding }}</span>
- </div>
- <div class="activity-item">
- <span class="activity-label">正在下载:</span>
- <span class="activity-value">{{ userActivity.downloading }}</span>
- </div>
- <div class="activity-item">
- <span class="activity-label">已上传:</span>
- <span class="activity-value">{{ userActivity.uploaded }}</span>
- </div>
- </div>
- <el-button type="primary" size="small" @click="navigateTo('/my-torrents')">
- 查看详情
- </el-button>
- </div>
-
- <div class="activity-card">
- <h3>邀请管理</h3>
- <div class="activity-stats">
- <div class="activity-item">
- <span class="activity-label">可用邀请:</span>
- <span class="activity-value">{{ userActivity.invitations }}</span>
- </div>
- <div class="activity-item">
- <span class="activity-label">已邀请:</span>
- <span class="activity-value">{{ userActivity.invited }}</span>
- </div>
- </div>
- <el-button type="success" size="small" @click="navigateTo('/invitations')">
- 管理邀请
- </el-button>
- </div>
- </div>
- </section>
- </main>
- </div>
- </div>
-</template>
-
-<script>
-import { ref, onMounted } from 'vue'
-import { useRouter } from 'vue-router'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import {
- UserFilled,
- ArrowDown,
- Download,
- Upload,
- ChatDotRound,
- Search,
- User,
- Connection,
- TrophyBase,
- ArrowRight,
- Clock,
- Coin
-} from '@element-plus/icons-vue'
-
-export default {
- name: 'HomeView',
- components: {
- UserFilled,
- ArrowDown
- },
- setup() {
- const router = useRouter()
-
- const userInfo = ref({
- username: '',
- loginTime: ''
- })
-
- const userStats = ref({
- uploaded: '128.5 GB',
- downloaded: '45.2 GB',
- ratio: '2.84'
- })
-
- const siteStats = ref({
- totalTorrents: '12,458',
- totalUsers: '8,924',
- onlineUsers: '342',
- totalSize: '45.2 TB'
- })
-
- const userActivity = ref({
- seeding: 15,
- downloading: 3,
- uploaded: 8,
- invitations: 5,
- invited: 12
- })
-
- const latestTorrents = ref([
- {
- id: 1,
- title: '[4K蓝光原盘] 阿凡达:水之道 Avatar: The Way of Water (2022)',
- category: '电影',
- uploader: 'MovieMaster',
- uploadTime: '2025-06-03T10:30:00',
- size: '85.6 GB',
- seeders: 128,
- leechers: 45,
- downloads: 892
- },
- {
- id: 2,
- title: '[FLAC] Taylor Swift - Midnights (Deluxe Edition) [2022]',
- category: '音乐',
- uploader: 'MusicLover',
- uploadTime: '2025-06-03T09:15:00',
- size: '1.2 GB',
- seeders: 67,
- leechers: 12,
- downloads: 456
- },
- {
- id: 3,
- title: '[合集] Adobe Creative Suite 2025 完整版',
- category: '软件',
- uploader: 'TechGuru',
- uploadTime: '2025-06-03T08:45:00',
- size: '12.8 GB',
- seeders: 234,
- leechers: 89,
- downloads: 1205
- }
- ])
-
- onMounted(() => {
- userInfo.value = {
- username: localStorage.getItem('username') || '用户',
- loginTime: localStorage.getItem('loginTime') || ''
- }
-
- // 模拟获取用户数据
- fetchUserData()
- })
-
- const fetchUserData = () => {
- // 这里应该是API调用,现在使用模拟数据
- console.log('获取用户数据...')
- }
-
- const formatTime = (timeString) => {
- if (!timeString) return ''
- const date = new Date(timeString)
- const now = new Date()
- const diff = now - date
- const hours = Math.floor(diff / (1000 * 60 * 60))
-
- if (hours < 1) return '刚刚'
- if (hours < 24) return `${hours}小时前`
- const days = Math.floor(hours / 24)
- return `${days}天前`
- }
-
- const getRatioClass = (ratio) => {
- const r = parseFloat(ratio)
- if (r >= 2) return 'excellent'
- if (r >= 1) return 'good'
- return 'warning'
- }
-
- const getCategoryType = (category) => {
- const types = {
- '电影': 'primary',
- '音乐': 'success',
- '软件': 'warning',
- '游戏': 'danger',
- '电视剧': 'info'
- }
- return types[category] || 'default'
- }
-
- const navigateTo = (path) => {
- router.push(path)
- }
-
- const handleUserCommand = async (command) => {
- switch (command) {
- case 'profile':
- navigateTo('/profile')
- break
- case 'settings':
- navigateTo('/settings')
- break
- case 'logout':
- await handleLogout()
- break
- }
- }
-
- const handleLogout = async () => {
- try {
- await ElMessageBox.confirm(
- '确定要退出登录吗?',
- '提示',
- {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }
- )
-
- localStorage.removeItem('isLoggedIn')
- localStorage.removeItem('username')
- localStorage.removeItem('loginTime')
- localStorage.removeItem('rememberLogin')
-
- ElMessage.success('已安全退出')
- router.push('/login')
-
- } catch {
- // 用户取消退出
- }
- }
-
- return {
- userInfo,
- userStats,
- siteStats,
- userActivity,
- latestTorrents,
- formatTime,
- getRatioClass,
- getCategoryType,
- navigateTo,
- handleUserCommand,
- handleLogout,
- Download,
- Upload,
- ChatDotRound,
- Search,
- User,
- Connection,
- TrophyBase,
- ArrowRight,
- Clock,
- Coin
- }
- }
-}
-</script>
-
-<style lang="scss" scoped>
-.home-page {
- min-height: 100vh;
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- padding: 0;
-}
-
-.home-container {
- max-width: 1200px;
- margin: 0 auto;
- background: #fff;
- min-height: 100vh;
-}
-
-// 网站头部
-.site-header {
- background: #fff;
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
- position: sticky;
- top: 0;
- z-index: 100;
-
- .header-content {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16px 24px;
-
- .logo-section {
- .site-logo {
- margin: 0;
- font-size: 24px;
- font-weight: bold;
- color: #667eea;
- }
- }
-
- .user-section {
- display: flex;
- align-items: center;
- gap: 24px;
-
- .user-stats {
- display: flex;
- gap: 16px;
-
- .stat-item {
- font-size: 14px;
-
- .stat-label {
- color: #909399;
- margin-right: 4px;
- }
-
- .stat-value {
- font-weight: 600;
- color: #2c3e50;
-
- &.ratio {
- &.excellent { color: #67c23a; }
- &.good { color: #e6a23c; }
- &.warning { color: #f56c6c; }
- }
- }
- }
- }
-
- .user-info {
- display: flex;
- align-items: center;
- gap: 8px;
- cursor: pointer;
-
- .username {
- font-weight: 500;
- color: #2c3e50;
- }
-
- .dropdown-icon {
- color: #909399;
- transition: transform 0.3s ease;
-
- &:hover {
- transform: rotate(180deg);
- }
- }
- }
- }
- }
-}
-
-// 主要内容
-.main-content {
- padding: 24px;
-}
-
-.section-title {
- font-size: 20px;
- font-weight: 600;
- color: #2c3e50;
- margin-bottom: 16px;
-}
-
-.section-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 16px;
-}
-
-// 统计概览
-.stats-overview {
- margin-bottom: 32px;
-
- .stats-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 16px;
-
- .stat-card {
- background: #fff;
- border-radius: 12px;
- padding: 24px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
- border: 1px solid #f0f0f0;
- display: flex;
- align-items: center;
- gap: 16px;
- transition: all 0.3s ease;
-
- &:hover {
- transform: translateY(-2px);
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
- }
-
- .stat-info {
- h3 {
- font-size: 24px;
- font-weight: bold;
- color: #2c3e50;
- margin: 0 0 4px 0;
- }
-
- p {
- font-size: 14px;
- color: #909399;
- margin: 0;
- }
- }
- }
- }
-}
-
-// 快速操作
-.quick-actions {
- margin-bottom: 32px;
-
- .action-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
- gap: 20px;
-
- .action-card {
- background: #fff;
- border-radius: 12px;
- padding: 32px 24px;
- text-align: center;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
- border: 1px solid #f0f0f0;
- cursor: pointer;
- transition: all 0.3s ease;
-
- &:hover {
- transform: translateY(-4px);
- box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
- }
-
- h3 {
- font-size: 18px;
- font-weight: 600;
- color: #2c3e50;
- margin: 16px 0 8px 0;
- }
-
- p {
- font-size: 14px;
- color: #7f8c8d;
- line-height: 1.5;
- margin: 0;
- }
- }
- }
-}
-
-// 最新种子
-.latest-torrents {
- margin-bottom: 32px;
-
- .torrents-list {
- background: #fff;
- border-radius: 12px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
- border: 1px solid #f0f0f0;
- overflow: hidden;
-
- .torrent-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20px 24px;
- border-bottom: 1px solid #f5f5f5;
- cursor: pointer;
- transition: background-color 0.3s ease;
-
- &:hover {
- background-color: #f8f9fa;
- }
-
- &:last-child {
- border-bottom: none;
- }
-
- .torrent-info {
- flex: 1;
-
- .torrent-category {
- margin-bottom: 8px;
- }
-
- .torrent-title {
- font-size: 16px;
- font-weight: 500;
- color: #2c3e50;
- margin: 0 0 8px 0;
- line-height: 1.4;
- }
-
- .torrent-meta {
- display: flex;
- gap: 16px;
-
- .meta-item {
- display: flex;
- align-items: center;
- gap: 4px;
- font-size: 12px;
- color: #909399;
-
- .el-icon {
- font-size: 12px;
- }
- }
- }
- }
-
- .torrent-stats {
- display: flex;
- gap: 24px;
-
- .stat-group {
- text-align: center;
-
- .stat-number {
- display: block;
- font-size: 16px;
- font-weight: 600;
-
- &.seeders { color: #67c23a; }
- &.leechers { color: #f56c6c; }
- }
-
- .stat-label {
- font-size: 12px;
- color: #909399;
- }
- }
- }
- }
- }
-}
-
-// 用户活动
-.user-activity {
- .activity-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
- gap: 20px;
-
- .activity-card {
- background: #fff;
- border-radius: 12px;
- padding: 24px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
- border: 1px solid #f0f0f0;
-
- h3 {
- font-size: 18px;
- font-weight: 600;
- color: #2c3e50;
- margin: 0 0 16px 0;
- }
-
- .activity-stats {
- margin-bottom: 16px;
-
- .activity-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 0;
-
- .activity-label {
- font-size: 14px;
- color: #7f8c8d;
- }
-
- .activity-value {
- font-size: 14px;
- font-weight: 600;
- color: #2c3e50;
- }
- }
- }
- }
- }
-}
-
-// 响应式设计
-@media (max-width: 768px) {
- .header-content {
- flex-direction: column;
- gap: 16px;
-
- .user-stats {
- flex-direction: column;
- gap: 8px;
- text-align: center;
- }
- }
-
- .main-content {
- padding: 16px;
- }
-
- .stats-grid,
- .action-grid,
- .activity-grid {
- grid-template-columns: 1fr;
- }
-
- .torrent-item {
- flex-direction: column;
- align-items: flex-start;
- gap: 16px;
-
- .torrent-stats {
- width: 100%;
- justify-content: space-around;
- }
- }
-}
+<template>
+ <div class="home-page">
+ <!-- 导航栏 -->
+ <div class="navbar">
+ <router-link to="/home" class="navbar-brand">PT Tracker</router-link>
+ <div class="navbar-nav">
+ <router-link to="/home" class="navbar-item">首页</router-link>
+ <router-link to="/torrents" class="navbar-item">种子</router-link>
+ <router-link to="/forum" class="navbar-item">论坛</router-link>
+ <el-dropdown @command="handleUserCommand">
+ <span class="navbar-user">
+ <el-avatar :size="32" :src="userAvatar">
+ {{ username.charAt(0).toUpperCase() }}
+ </el-avatar>
+ <span class="username">{{ username }}</span>
+ <el-icon><ArrowDown /></el-icon>
+ </span>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <el-dropdown-item command="profile">
+ <el-icon><User /></el-icon>
+ 个人资料
+ </el-dropdown-item>
+ <el-dropdown-item command="settings">
+ <el-icon><Setting /></el-icon>
+ 设置
+ </el-dropdown-item>
+ <el-dropdown-item divided command="logout">
+ <el-icon><SwitchButton /></el-icon>
+ 退出登录
+ </el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ </div>
+ </div>
+
+ <!-- 主内容区 -->
+ <div class="main-content">
+ <!-- 欢迎卡片 -->
+ <div class="welcome-card">
+ <div class="welcome-header">
+ <h1>欢迎回来,{{ username }}!</h1>
+ <p>当前时间:{{ currentTime }}</p>
+ </div>
+
+ <!-- 用户统计概览 -->
+ <div class="stats-overview" v-if="userInfo">
+ <div class="stat-item">
+ <div class="stat-icon upload">
+ <el-icon size="24"><Upload /></el-icon>
+ </div>
+ <div class="stat-content">
+ <h3>{{ formatBytes(userInfo.user.uploaded) }}</h3>
+ <p>总上传</p>
+ </div>
+ </div>
+
+ <div class="stat-item">
+ <div class="stat-icon download">
+ <el-icon size="24"><Download /></el-icon>
+ </div>
+ <div class="stat-content">
+ <h3>{{ formatBytes(userInfo.user.downloaded) }}</h3>
+ <p>总下载</p>
+ </div>
+ </div>
+
+ <div class="stat-item">
+ <div class="stat-icon ratio">
+ <el-icon size="24"><TrendCharts /></el-icon>
+ </div>
+ <div class="stat-content">
+ <h3>{{ calculateRatio(userInfo.user.uploaded, userInfo.user.downloaded) }}</h3>
+ <p>分享率</p>
+ </div>
+ </div>
+
+ <div class="stat-item">
+ <div class="stat-icon points">
+ <el-icon size="24"><Star /></el-icon>
+ </div>
+ <div class="stat-content">
+ <h3>{{ userInfo.user.karma || '0' }}</h3>
+ <p>积分</p>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 功能快捷入口 -->
+ <div class="quick-actions">
+ <h2>快捷操作</h2>
+ <div class="actions-grid">
+ <div class="action-card" @click="$router.push('/upload')">
+ <el-icon size="32" color="#67c23a"><Upload /></el-icon>
+ <h3>上传种子</h3>
+ <p>分享你的资源</p>
+ </div>
+
+ <div class="action-card" @click="$router.push('/torrents')">
+ <el-icon size="32" color="#409eff"><Search /></el-icon>
+ <h3>浏览种子</h3>
+ <p>发现新内容</p>
+ </div>
+
+ <div class="action-card" @click="$router.push('/forum')">
+ <el-icon size="32" color="#e6a23c"><ChatDotRound /></el-icon>
+ <h3>社区论坛</h3>
+ <p>交流讨论</p>
+ </div>
+
+ <div class="action-card" @click="$router.push('/profile')">
+ <el-icon size="32" color="#f56c6c"><User /></el-icon>
+ <h3>个人中心</h3>
+ <p>管理账户</p>
+ </div>
+ </div>
+ </div>
+
+ <!-- API连接状态测试 -->
+ <div class="api-status-card">
+ <h2>API连接状态</h2>
+ <div class="status-items">
+ <div class="status-item">
+ <el-icon :color="loginStatusColor"><CircleCheck /></el-icon>
+ <span>登录状态:{{ loginStatusText }}</span>
+ <el-button size="small" @click="checkLoginStatus">检查状态</el-button>
+ </div>
+
+ <div class="status-item">
+ <el-icon :color="userInfoStatusColor"><User /></el-icon>
+ <span>用户信息:{{ userInfoStatusText }}</span>
+ <el-button size="small" @click="refreshUserInfo">刷新信息</el-button>
+ </div>
+ </div>
+
+ <!-- 用户详细信息展示 -->
+ <div class="user-details" v-if="userInfo">
+ <h3>用户详细信息</h3>
+ <div class="details-grid">
+ <div class="detail-item">
+ <label>用户ID:</label>
+ <span>{{ userInfo.user.id }}</span>
+ </div>
+ <div class="detail-item">
+ <label>用户名:</label>
+ <span>{{ userInfo.user.username }}</span>
+ </div>
+ <div class="detail-item">
+ <label>邮箱:</label>
+ <span>{{ userInfo.user.email }}</span>
+ </div>
+ <div class="detail-item">
+ <label>用户组:</label>
+ <span>{{ userInfo.user.group?.displayName || '未知' }}</span>
+ </div>
+ <div class="detail-item">
+ <label>注册时间:</label>
+ <span>{{ formatDate(userInfo.user.createdAt) }}</span>
+ </div>
+ <div class="detail-item">
+ <label>个性签名:</label>
+ <span>{{ userInfo.user.signature || '这个用户很懒,还没有个性签名' }}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+import { ref, computed, onMounted, onUnmounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { useStore } from 'vuex'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import {
+ ArrowDown,
+ User,
+ Setting,
+ SwitchButton,
+ Upload,
+ Download,
+ TrendCharts,
+ Star,
+ Search,
+ ChatDotRound,
+ CircleCheck
+} from '@element-plus/icons-vue'
+
+export default {
+ name: 'HomeView',
+ components: {
+ ArrowDown,
+ User,
+ Setting,
+ SwitchButton,
+ Upload,
+ Download,
+ TrendCharts,
+ Star,
+ Search,
+ ChatDotRound,
+ CircleCheck
+ },
+ setup() {
+ const router = useRouter()
+ const store = useStore()
+
+ const currentTime = ref('')
+ const timeInterval = ref(null)
+
+ // 从store获取用户信息
+ const userInfo = computed(() => store.getters['auth/userInfo'])
+ const username = computed(() => store.getters['auth/username'])
+ const userAvatar = computed(() => store.getters['auth/avatar'])
+ const isAuthenticated = computed(() => store.getters['auth/isAuthenticated'])
+
+ // API状态
+ const loginStatusColor = computed(() => isAuthenticated.value ? '#67c23a' : '#f56c6c')
+ const loginStatusText = computed(() => isAuthenticated.value ? '已登录' : '未登录')
+
+ const userInfoStatusColor = computed(() => userInfo.value ? '#67c23a' : '#f56c6c')
+ const userInfoStatusText = computed(() => userInfo.value ? '已获取' : '未获取')
+
+ // 更新当前时间
+ const updateCurrentTime = () => {
+ currentTime.value = new Date().toLocaleString('zh-CN')
+ }
+
+ // 格式化字节数
+ const formatBytes = (bytes) => {
+ if (!bytes || bytes === 0) return '0 B'
+
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
+ const i = Math.floor(Math.log(bytes) / Math.log(1024))
+ const size = (bytes / Math.pow(1024, i)).toFixed(2)
+
+ return `${size} ${sizes[i]}`
+ }
+
+ // 计算分享率
+ const calculateRatio = (uploaded, downloaded) => {
+ if (!uploaded || !downloaded || downloaded === 0) {
+ return uploaded > 0 ? '∞' : '0.00'
+ }
+ return (uploaded / downloaded).toFixed(2)
+ }
+
+ // 格式化日期
+ const formatDate = (timestamp) => {
+ if (!timestamp) return '未知'
+ return new Date(timestamp).toLocaleDateString('zh-CN')
+ }
+
+ // 检查登录状态
+ const checkLoginStatus = async () => {
+ try {
+ await store.dispatch('auth/checkLoginStatus')
+ ElMessage.success('登录状态检查完成')
+ } catch (error) {
+ console.error('检查登录状态失败:', error)
+ ElMessage.error('检查登录状态失败')
+ }
+ }
+
+ // 刷新用户信息
+ const refreshUserInfo = async () => {
+ try {
+ await store.dispatch('auth/checkLoginStatus')
+ ElMessage.success('用户信息刷新成功')
+ } catch (error) {
+ console.error('刷新用户信息失败:', error)
+ ElMessage.error('刷新用户信息失败')
+ }
+ }
+
+ // 处理用户菜单命令
+ const handleUserCommand = async (command) => {
+ switch (command) {
+ case 'profile':
+ router.push('/profile')
+ break
+ case 'settings':
+ ElMessage.info('设置功能开发中...')
+ break
+ case 'logout':
+ try {
+ await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning'
+ })
+
+ await store.dispatch('auth/logout')
+ router.push('/login')
+ } catch (error) {
+ // 用户取消操作
+ if (error !== 'cancel') {
+ console.error('退出登录失败:', error)
+ }
+ }
+ break
+ }
+ }
+
+ onMounted(() => {
+ // 开始时间更新
+ updateCurrentTime()
+ timeInterval.value = setInterval(updateCurrentTime, 1000)
+
+ // 检查登录状态
+ if (!isAuthenticated.value) {
+ checkLoginStatus()
+ }
+ })
+
+ onUnmounted(() => {
+ // 清理定时器
+ if (timeInterval.value) {
+ clearInterval(timeInterval.value)
+ }
+ })
+
+ return {
+ currentTime,
+ userInfo,
+ username,
+ userAvatar,
+ isAuthenticated,
+ loginStatusColor,
+ loginStatusText,
+ userInfoStatusColor,
+ userInfoStatusText,
+ formatBytes,
+ calculateRatio,
+ formatDate,
+ checkLoginStatus,
+ refreshUserInfo,
+ handleUserCommand
+ }
+ }
+}
+</script>
+
+<style lang="scss" scoped>
+.home-page {
+ min-height: 100vh;
+ background: #f5f5f5;
+}
+
+.navbar {
+ background: #fff;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+ height: 60px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 24px;
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+}
+
+.navbar-brand {
+ font-size: 20px;
+ font-weight: 700;
+ color: #409eff;
+ text-decoration: none;
+}
+
+.navbar-nav {
+ display: flex;
+ align-items: center;
+ gap: 24px;
+}
+
+.navbar-item {
+ color: #606266;
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.3s;
+
+ &:hover {
+ color: #409eff;
+ }
+}
+
+.navbar-user {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 6px;
+ transition: background-color 0.3s;
+
+ &:hover {
+ background-color: #f5f7fa;
+ }
+
+ .username {
+ font-weight: 500;
+ color: #303133;
+ }
+}
+
+.main-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 24px;
+}
+
+.welcome-card {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ border-radius: 12px;
+ padding: 32px;
+ margin-bottom: 24px;
+
+ .welcome-header {
+ text-align: center;
+ margin-bottom: 24px;
+
+ h1 {
+ font-size: 28px;
+ margin-bottom: 8px;
+ }
+
+ p {
+ opacity: 0.9;
+ font-size: 14px;
+ }
+ }
+
+ .stats-overview {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 16px;
+
+ .stat-item {
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(10px);
+ border-radius: 8px;
+ padding: 20px;
+ display: flex;
+ align-items: center;
+ gap: 16px;
+
+ .stat-icon {
+ width: 48px;
+ height: 48px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .stat-content {
+ h3 {
+ font-size: 20px;
+ font-weight: 600;
+ margin: 0 0 4px 0;
+ }
+
+ p {
+ font-size: 14px;
+ opacity: 0.8;
+ margin: 0;
+ }
+ }
+ }
+ }
+}
+
+.quick-actions {
+ background: #fff;
+ border-radius: 12px;
+ padding: 24px;
+ margin-bottom: 24px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+
+ h2 {
+ font-size: 20px;
+ color: #303133;
+ margin: 0 0 20px 0;
+ }
+
+ .actions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 16px;
+
+ .action-card {
+ background: #f8f9fa;
+ border-radius: 8px;
+ padding: 24px;
+ text-align: center;
+ cursor: pointer;
+ transition: all 0.3s;
+ border: 2px solid transparent;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+ border-color: #409eff;
+ }
+
+ h3 {
+ font-size: 16px;
+ color: #303133;
+ margin: 12px 0 8px 0;
+ }
+
+ p {
+ font-size: 14px;
+ color: #909399;
+ margin: 0;
+ }
+ }
+ }
+}
+
+.api-status-card {
+ background: #fff;
+ border-radius: 12px;
+ padding: 24px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+
+ h2 {
+ font-size: 20px;
+ color: #303133;
+ margin: 0 0 20px 0;
+ }
+
+ .status-items {
+ margin-bottom: 24px;
+
+ .status-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 0;
+ border-bottom: 1px solid #ebeef5;
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ span {
+ flex: 1;
+ font-size: 14px;
+ color: #606266;
+ }
+ }
+ }
+
+ .user-details {
+ border-top: 1px solid #ebeef5;
+ padding-top: 20px;
+
+ h3 {
+ font-size: 16px;
+ color: #303133;
+ margin: 0 0 16px 0;
+ }
+
+ .details-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 12px;
+
+ .detail-item {
+ display: flex;
+ align-items: center;
+ padding: 8px 0;
+
+ label {
+ font-weight: 500;
+ color: #909399;
+ min-width: 80px;
+ font-size: 14px;
+ }
+
+ span {
+ color: #606266;
+ font-size: 14px;
+ }
+ }
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .navbar {
+ padding: 0 16px;
+
+ .navbar-nav {
+ gap: 16px;
+ }
+
+ .navbar-user .username {
+ display: none;
+ }
+ }
+
+ .main-content {
+ padding: 16px;
+ }
+
+ .welcome-card {
+ padding: 24px 16px;
+
+ .welcome-header h1 {
+ font-size: 24px;
+ }
+
+ .stats-overview {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 12px;
+
+ .stat-item {
+ padding: 16px;
+
+ .stat-content h3 {
+ font-size: 16px;
+ }
+ }
+ }
+ }
+
+ .actions-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 12px;
+
+ .action-card {
+ padding: 16px;
+
+ h3 {
+ font-size: 14px;
+ }
+
+ p {
+ font-size: 12px;
+ }
+ }
+ }
+
+ .details-grid {
+ grid-template-columns: 1fr;
+ }
+}
+
+@media (max-width: 480px) {
+ .stats-overview {
+ grid-template-columns: 1fr;
+ }
+
+ .actions-grid {
+ grid-template-columns: 1fr;
+ }
+}
</style>
\ No newline at end of file