blob: 1828d289a692769a09289d7e3edd8f07ba1dc9be [file] [log] [blame] [edit]
<template>
<div class="home-page">
<!-- 导航栏 -->
<Navbar />
<!-- 主内容区 -->
<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 class="action-card" @click="$router.push('/chat')">
<el-icon size="32" color="#8e44ad"><ChatDotRound /></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'
import Navbar from "@/components/Navbar.vue";
export default {
name: 'HomeView',
components: {
ArrowDown,
User,
Setting,
SwitchButton,
Upload,
Download,
TrendCharts,
Star,
Search,
ChatDotRound,
CircleCheck,
Navbar
},
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>