Files
EchoChat/admin/src/views/layout/index.vue
2026-05-18 19:42:55 +08:00

273 lines
6.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
管理后台主布局组件
设计系统design-system/echochat/MASTER.md管理端适配
结构
- 左侧固定侧边栏可折叠logo + 导航菜单
- 右侧主区域顶部栏面包屑 + 管理员信息+ 内容区
使用 Element Plus Container 布局组件
-->
<template>
<el-container class="layout-container">
<!-- 左侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '220px'" class="layout-aside">
<div class="logo-section">
<div class="logo-icon">E</div>
<span v-show="!isCollapse" class="logo-text">EchoChat</span>
</div>
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
router
background-color="#1E293B"
text-color="#94A3B8"
active-text-color="#FFFFFF"
class="aside-menu"
>
<el-menu-item index="/dashboard">
<el-icon><DataAnalysis /></el-icon>
<template #title>仪表盘</template>
</el-menu-item>
<el-sub-menu index="user-manage">
<template #title>
<el-icon><User /></el-icon>
<span>用户管理</span>
</template>
<el-menu-item index="/user/list">用户列表</el-menu-item>
</el-sub-menu>
<el-sub-menu index="contact-manage">
<template #title>
<el-icon><Connection /></el-icon>
<span>联系人管理</span>
</template>
<el-menu-item index="/contact/list">好友关系</el-menu-item>
</el-sub-menu>
<el-sub-menu index="group-manage">
<template #title>
<el-icon><ChatDotSquare /></el-icon>
<span>群组管理</span>
</template>
<el-menu-item index="/group/list">群组列表</el-menu-item>
</el-sub-menu>
<el-sub-menu index="message-manage">
<template #title>
<el-icon><ChatRound /></el-icon>
<span>消息管理</span>
</template>
<el-menu-item index="/message/list">消息列表</el-menu-item>
<el-menu-item index="/message/stats">消息统计</el-menu-item>
</el-sub-menu>
<el-sub-menu index="meeting-manage">
<template #title>
<el-icon><VideoCamera /></el-icon>
<span>会议管理</span>
</template>
<el-menu-item index="/meeting/list">会议记录</el-menu-item>
</el-sub-menu>
<el-menu-item index="/monitor/online">
<el-icon><Monitor /></el-icon>
<template #title>在线监控</template>
</el-menu-item>
</el-menu>
</el-aside>
<!-- 右侧主区域 -->
<el-container class="layout-main">
<!-- 顶部栏 -->
<el-header class="layout-header">
<div class="header-left">
<el-icon class="collapse-btn" @click="isCollapse = !isCollapse">
<Expand v-if="isCollapse" />
<Fold v-else />
</el-icon>
</div>
<div class="header-right">
<el-dropdown trigger="click" @command="handleCommand">
<span class="admin-info">
<el-avatar :size="32" class="admin-avatar">
{{ avatarLetter }}
</el-avatar>
<span class="admin-name">{{ username }}</span>
<el-icon><ArrowDown /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<!-- 内容区 -->
<el-main class="layout-content">
<router-view />
</el-main>
</el-container>
</el-container>
</template>
<script setup>
/**
* 管理后台布局逻辑
*
* 侧边栏折叠状态
* 管理员信息从 Store 获取
* 退出登录后清除状态并跳转
*/
import { ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useUserStore } from '@/store/user'
const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
/** 侧边栏折叠状态 */
const isCollapse = ref(false)
/** 当前激活的菜单项(跟随路由) */
const activeMenu = computed(() => route.path)
/** 管理员用户名 */
const username = computed(() => userStore.username || '管理员')
/** 头像占位字母 */
const avatarLetter = computed(() => {
const name = userStore.userInfo?.nickname || userStore.username || '?'
return name.charAt(0).toUpperCase()
})
/**
* 下拉菜单命令处理
* @param {string} command
*/
const handleCommand = async (command) => {
if (command === 'logout') {
await userStore.logout()
router.push('/login')
}
}
</script>
<style scoped>
/*
* 管理端布局样式
* 左侧导航:深色 #1E293B
* 顶部栏:白色 + 底部阴影
* 内容区:灰色背景 #F0F2F5
*/
.layout-container {
min-height: 100vh;
}
/* ---- 侧边栏 ---- */
.layout-aside {
background-color: #1E293B;
transition: width 0.3s ease;
overflow: hidden;
}
.logo-section {
display: flex;
align-items: center;
justify-content: center;
height: 60px;
padding: 0 16px;
border-bottom: 1px solid #334155;
}
.logo-icon {
width: 32px;
height: 32px;
border-radius: 6px;
background-color: #2563EB;
color: #FFFFFF;
font-size: 18px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.logo-text {
font-size: 16px;
font-weight: 600;
color: #FFFFFF;
margin-left: 10px;
white-space: nowrap;
}
.aside-menu {
border-right: none !important;
}
/* ---- 顶部栏 ---- */
.layout-header {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #FFFFFF;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
padding: 0 20px;
height: 60px;
}
.header-left {
display: flex;
align-items: center;
}
.collapse-btn {
font-size: 20px;
cursor: pointer;
color: #64748B;
transition: color 200ms;
}
.collapse-btn:hover {
color: #2563EB;
}
.header-right {
display: flex;
align-items: center;
}
.admin-info {
display: flex;
align-items: center;
cursor: pointer;
gap: 8px;
}
.admin-avatar {
background-color: #2563EB;
color: #FFFFFF;
font-weight: 600;
}
.admin-name {
font-size: 14px;
color: #475569;
}
/* ---- 内容区 ---- */
.layout-content {
background-color: #F0F2F5;
padding: 20px;
min-height: calc(100vh - 60px);
}
</style>