You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 lines
7.4 KiB
Vue

<template>
<el-container class="layout-container">
<el-aside :width="isCollapsed ? '64px' : '200px'" class="aside">
<div class="logo">
<svg t="1776756485739" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2128" width="32" height="32"><path d="M512 40.832a381.76 381.76 0 1 0 381.76 381.76A381.824 381.824 0 0 0 512 40.832zM307.2 206.208a44.8 44.8 0 1 1 44.8 44.8 44.8 44.8 0 0 1-44.8-44.8zM512 582.272a159.68 159.68 0 1 1 159.68-159.68A159.68 159.68 0 0 1 512 582.272z m0 0" fill="#409EFF" p-id="2129"></path><path d="M797.312 761.728a380.416 380.416 0 0 1-567.104 4.16l-70.4 161.216c-12.8 30.016 12.8 56.128 57.6 56.128h593.984c45.312 0 70.976-26.112 57.6-56.128z m0 0" fill="#409EFF" p-id="2130"></path></svg>
<span v-if="!isCollapsed"></span>
</div>
<el-menu
:default-active="activeMenu"
:collapse="isCollapsed"
:collapse-transition="false"
router
class="menu"
>
<el-menu-item index="/home">
<el-icon><HomeFilled /></el-icon>
<template #title>首页</template>
</el-menu-item>
<el-menu-item index="/dataset">
<el-icon><FolderOpened /></el-icon>
<template #title>数据集</template>
</el-menu-item>
<el-menu-item index="/face-recognition">
<el-icon><UserFilled /></el-icon>
<template #title>人脸识别</template>
</el-menu-item>
<el-menu-item index="/personnel">
<el-icon><Avatar /></el-icon>
<template #title>人员管理</template>
</el-menu-item>
<el-menu-item index="/smart-tracking">
<el-icon><Aim /></el-icon>
<template #title>智能追踪</template>
</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header class="header">
<div class="header-left">
<el-icon class="collapse-btn" @click="isCollapsed = !isCollapsed">
<Fold v-if="!isCollapsed" />
<Expand v-else />
</el-icon>
</div>
<div class="header-right">
<!-- <el-popover
placement="bottom"
:width="300"
trigger="click"
>
<template #reference>
<el-icon class="header-icon"><Brush /></el-icon>
</template>
<div class="theme-picker">
<h4>切换主题颜色</h4>
<div class="color-list">
<div
v-for="item in themeStore.themeColors"
:key="item.color"
class="color-item"
:style="{ backgroundColor: item.color }"
:class="{ active: item.color === themeStore.themeColor }"
@click="handleChangeTheme(item.color)"
>
<el-icon v-if="item.color === themeStore.themeColor"><Check /></el-icon>
</div>
</div>
</div>
</el-popover> -->
<el-dropdown @command="handleCommand">
<span class="user-info">
<el-avatar :size="32" icon="UserFilled" />
<span class="username">{{ userStore.userInfo?.userName || '用户' }}</span>
<el-icon><ArrowDown /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">
<el-icon><User /></el-icon>个人中心
</el-dropdown-item>
<el-dropdown-item command="settings">
<el-icon><Setting /></el-icon>设置
</el-dropdown-item>
<el-dropdown-item divided command="logout">
<el-icon><SwitchButton /></el-icon>退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
<el-main class="main">
<router-view />
</el-main>
</el-container>
</el-container>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessageBox, ElMessage } from 'element-plus'
import { useUserStore } from '@/stores/user'
import { useThemeStore } from '@/stores/theme'
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
const themeStore = useThemeStore()
const isCollapsed = ref(false)
const activeMenu = computed(() => route.path)
const handleChangeTheme = (color) => {
themeStore.setThemeColor(color)
ElMessage.success('主题颜色已切换')
}
const handleCommand = (command) => {
switch (command) {
case 'logout':
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
userStore.logout()
router.push('/login')
ElMessage.success('已退出登录')
}).catch(() => {})
break
case 'profile':
ElMessage.info('个人中心功能开发中')
break
case 'settings':
ElMessage.info('设置功能开发中')
break
}
}
</script>
<style lang="scss" scoped>
@use '@/styles/variables.scss' as *;
@use '@/styles/mixin.scss' as *;
.layout-container {
height: 100vh;
}
.aside {
background: #fff;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05);
transition: width 0.3s;
overflow: hidden;
.logo {
height: 60px;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 18px;
font-weight: 600;
color: var(--el-color-primary);
border-bottom: 1px solid $border-light;
img {
width: 32px;
height: 32px;
}
}
.menu {
border-right: none;
}
}
.header {
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
.header-left {
.collapse-btn {
font-size: 20px;
cursor: pointer;
color: $text-regular;
transition: color 0.3s;
&:hover {
color: $primary-color;
}
}
}
.header-right {
display: flex;
align-items: center;
gap: 20px;
.header-icon {
font-size: 20px;
cursor: pointer;
color: $text-regular;
transition: color 0.3s;
&:hover {
color: $primary-color;
}
}
.user-info {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
.username {
font-size: 14px;
color: $text-regular;
}
}
}
}
.main {
background: $bg-base;
padding: 20px;
}
.theme-picker {
h4 {
font-size: 14px;
font-weight: 500;
margin-bottom: 15px;
color: $text-primary;
}
.color-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
.color-item {
width: 50px;
height: 50px;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
transition: all 0.3s;
border: 2px solid transparent;
&:hover {
transform: scale(1.1);
}
&.active {
border-color: $text-primary;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
}
}
}
:deep(.el-menu--collapse .el-menu-item span) {
display: none;
}
</style>