You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
5.3 KiB
264 lines
5.3 KiB
<script setup lang="ts">
|
|
import { ref, onMounted } from 'vue';
|
|
import { useRouter, useRoute } from 'vue-router';
|
|
import { useUserStore } from '../stores';
|
|
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const userStore = useUserStore();
|
|
|
|
// 侧边栏菜单项
|
|
const menuItems = [
|
|
{ name: 'Home', title: '个人主页', icon: 'home' },
|
|
{ name: 'AccountManager', title: '账号管理', icon: 'user' },
|
|
{ name: 'Posts', title: '我的帖子', icon: 'document' },
|
|
{ name: 'Messages', title: '消息中心', icon: 'message' },
|
|
{ name: 'Settings', title: '设置', icon: 'setting' }
|
|
];
|
|
|
|
// 当前激活的菜单项
|
|
const activeIndex = ref(0);
|
|
|
|
// 设置激活的菜单项
|
|
const setActive = (index: number) => {
|
|
activeIndex.value = index;
|
|
router.push({ name: menuItems[index].name });
|
|
};
|
|
|
|
// 根据当前路由设置激活的菜单项
|
|
onMounted(() => {
|
|
const currentRouteName = route.name as string;
|
|
const index = menuItems.findIndex(item => item.name === currentRouteName);
|
|
if (index !== -1) {
|
|
activeIndex.value = index;
|
|
}
|
|
|
|
// 如果已登录,获取用户信息
|
|
if (userStore.isLoggedIn) {
|
|
userStore.fetchUserInfo();
|
|
} else {
|
|
// 未登录则跳转到登录页
|
|
router.push('/login');
|
|
}
|
|
});
|
|
|
|
// 退出登录
|
|
const logout = () => {
|
|
userStore.logout();
|
|
router.push('/login');
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="personal-layout">
|
|
<!-- 侧边栏 -->
|
|
<div class="sidebar">
|
|
<div class="sidebar-header">
|
|
<div class="avatar">
|
|
<img :src="userStore.userInfo?.avatar || '/images/默认头像.jpg'" alt="用户头像">
|
|
</div>
|
|
<div class="username">{{ userStore.userInfo?.username || '用户' }}</div>
|
|
</div>
|
|
|
|
<ul class="menu">
|
|
<li
|
|
v-for="(item, index) in menuItems"
|
|
:key="index"
|
|
:class="{ active: activeIndex === index }"
|
|
@click="setActive(index)"
|
|
>
|
|
<div class="menu-item">
|
|
<div class="icon">
|
|
<el-icon>
|
|
<component :is="item.icon"></component>
|
|
</el-icon>
|
|
</div>
|
|
<div class="title">{{ item.title }}</div>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="sidebar-footer">
|
|
<button class="logout-btn" @click="logout">
|
|
<el-icon><switch-button /></el-icon>
|
|
<span>退出登录</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 主内容区 -->
|
|
<div class="main-content">
|
|
<router-view />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.personal-layout {
|
|
display: flex;
|
|
width: 100%;
|
|
height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sidebar {
|
|
width: var(--sidebar-width);
|
|
height: 100%;
|
|
background-color: var(--secondary-color);
|
|
transition: width var(--transition-slow);
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
z-index: 100;
|
|
}
|
|
|
|
.sidebar:hover {
|
|
width: var(--sidebar-width-expanded);
|
|
}
|
|
|
|
.sidebar-header {
|
|
padding: var(--spacing-lg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.avatar {
|
|
width: 60px;
|
|
height: 60px;
|
|
border-radius: var(--border-radius-full);
|
|
overflow: hidden;
|
|
margin-bottom: var(--spacing-md);
|
|
border: 3px solid var(--primary-light);
|
|
}
|
|
|
|
.avatar img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.username {
|
|
color: var(--text-primary);
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.menu {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 0 var(--spacing-sm);
|
|
}
|
|
|
|
.menu li {
|
|
position: relative;
|
|
margin-bottom: var(--spacing-md);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: var(--spacing-md);
|
|
border-radius: var(--border-radius-md);
|
|
transition: background-color var(--transition-normal);
|
|
}
|
|
|
|
.menu-item:hover {
|
|
background-color: rgba(255, 255, 255, 0.5);
|
|
}
|
|
|
|
.menu li.active .menu-item {
|
|
background-color: var(--primary-color);
|
|
color: white;
|
|
}
|
|
|
|
.icon {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 24px;
|
|
height: 24px;
|
|
margin-right: var(--spacing-md);
|
|
}
|
|
|
|
.title {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.sidebar-footer {
|
|
padding: var(--spacing-lg);
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.logout-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: var(--spacing-sm) var(--spacing-md);
|
|
border-radius: var(--border-radius-md);
|
|
background-color: rgba(255, 255, 255, 0.5);
|
|
color: var(--text-primary);
|
|
transition: background-color var(--transition-normal);
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
background-color: rgba(255, 255, 255, 0.8);
|
|
}
|
|
|
|
.logout-btn .el-icon {
|
|
margin-right: var(--spacing-sm);
|
|
}
|
|
|
|
.main-content {
|
|
flex: 1;
|
|
padding: var(--spacing-xl);
|
|
overflow-y: auto;
|
|
background-color: var(--bg-secondary);
|
|
}
|
|
|
|
/* 响应式设计 */
|
|
@media (max-width: 768px) {
|
|
.personal-layout {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.sidebar {
|
|
width: 100%;
|
|
height: auto;
|
|
flex-direction: row;
|
|
padding: var(--spacing-sm);
|
|
}
|
|
|
|
.sidebar:hover {
|
|
width: 100%;
|
|
}
|
|
|
|
.sidebar-header {
|
|
margin-bottom: 0;
|
|
padding: var(--spacing-sm);
|
|
}
|
|
|
|
.menu {
|
|
flex-direction: row;
|
|
padding: 0;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.menu li {
|
|
margin-right: var(--spacing-md);
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.sidebar-footer {
|
|
padding: var(--spacing-sm);
|
|
}
|
|
|
|
.main-content {
|
|
padding: var(--spacing-md);
|
|
}
|
|
}
|
|
</style>
|