feat: Phase 1 完成 — 基础设施 + 用户认证 + 管理端 + 端到端验证
Phase 1 (基础设施与用户认证) 全部 11 个 Task 开发完成: 后端 (Go): - Auth 模块: 注册/登录/JWT(有状态)/Profile/密码修改 - Admin 模块: 用户列表/详情/禁用/启用/角色分配/创建用户 - 中间件: JWT认证 + RBAC角色权限 + 请求日志 + CORS + Panic恢复 - Dockerfile 多阶段构建 + Docker Compose 全栈部署 前台 (uni-app): - 登录/注册页面 + 自定义 TabBar + 个人中心 - 请求封装 + 状态管理 (Pinia) 管理端 (Vue 3 + Element Plus): - 登录/仪表盘/用户列表/用户详情 - Axios 封装 + 路由守卫 + Pinia 状态管理 验证: - 全流程 API 端到端测试通过 - Playwright 页面自动化验证通过 - code-reviewer 代码审查通过 Made-with: Cursor
This commit is contained in:
234
admin/src/views/layout/index.vue
Normal file
234
admin/src/views/layout/index.vue
Normal file
@@ -0,0 +1,234 @@
|
||||
<!--
|
||||
管理后台主布局组件
|
||||
|
||||
设计系统: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-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>
|
||||
Reference in New Issue
Block a user