论坛,聊天室前后端对接
Change-Id: I90740329ab40dc050e8a791a382ab187900d673a
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
index f339463..de56055 100644
--- a/src/views/HomeView.vue
+++ b/src/views/HomeView.vue
@@ -1,635 +1,640 @@
-<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>
- </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 Navbar from '@/components/Navbar.vue'
-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,
- 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;
- }
-}
+<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>
\ No newline at end of file