336 lines
No EOL
7.5 KiB
Vue
336 lines
No EOL
7.5 KiB
Vue
<template>
|
|
<el-container class="layout-container">
|
|
<div
|
|
v-show="isMobile && !isCollapse"
|
|
class="mobile-mask"
|
|
@click="toggleSidebar">
|
|
</div>
|
|
|
|
<el-aside
|
|
:width="isCollapse ? '64px' : '220px'"
|
|
class="aside"
|
|
:class="{ 'mobile-aside': isMobile, 'mobile-collapsed': isMobile && isCollapse }">
|
|
<div class="logo">
|
|
<img src="../assets/logo.png" alt="logo" class="logo-img">
|
|
<span class="logo-text" v-show="!isCollapse">iFileProxy<br />数据管理系统</span>
|
|
</div>
|
|
<el-menu
|
|
router
|
|
:default-active="$route.path"
|
|
class="el-menu-vertical"
|
|
background-color="#001529"
|
|
text-color="rgba(255,255,255,0.65)"
|
|
active-text-color="#fff"
|
|
:collapse="isCollapse">
|
|
<el-menu-item :index="`${ADMIN_ROUTE_BASE}/dashboard`">
|
|
<el-icon><DataLine /></el-icon>
|
|
<span>概览面板</span>
|
|
</el-menu-item>
|
|
<template v-if="userRole === 'admin'">
|
|
<el-menu-item :index="`${ADMIN_ROUTE_BASE}/tasks`">
|
|
<el-icon><List /></el-icon>
|
|
<span>任务管理</span>
|
|
</el-menu-item>
|
|
<el-menu-item :index="`${ADMIN_ROUTE_BASE}/config`">
|
|
<el-icon><Setting /></el-icon>
|
|
<span>系统配置</span>
|
|
</el-menu-item>
|
|
<template v-if="userInfo.mask === 2">
|
|
<el-menu-item :index="`${ADMIN_ROUTE_BASE}/users`">
|
|
<el-icon><User /></el-icon>
|
|
<span>用户管理</span>
|
|
</el-menu-item>
|
|
</template>
|
|
<el-menu-item :index="`${ADMIN_ROUTE_BASE}/events`">
|
|
<el-icon><List /></el-icon>
|
|
<span>事件记录</span>
|
|
</el-menu-item>
|
|
</template>
|
|
</el-menu>
|
|
</el-aside>
|
|
|
|
<el-container>
|
|
<el-header class="header">
|
|
<div class="header-left">
|
|
<el-icon
|
|
class="toggle-icon"
|
|
@click="toggleSidebar">
|
|
<component :is="isCollapse ? 'Expand' : 'Fold'" />
|
|
</el-icon>
|
|
</div>
|
|
<div class="header-right">
|
|
<el-dropdown>
|
|
<span class="user-info">
|
|
<el-avatar size="small" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
|
|
<span class="username">你好,
|
|
{{ userInfo.nickname }}
|
|
</span>
|
|
</span>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item @click="router.push(`${ADMIN_ROUTE_BASE}/profile`)">
|
|
<el-icon><User /></el-icon>个人信息
|
|
</el-dropdown-item>
|
|
<el-dropdown-item divided @click="handleLogout">
|
|
<el-icon><SwitchButton /></el-icon>退出登录
|
|
</el-dropdown-item>
|
|
</el-dropdown-menu>
|
|
</template>
|
|
</el-dropdown>
|
|
</div>
|
|
</el-header>
|
|
<el-main class="main-container">
|
|
<router-view></router-view>
|
|
</el-main>
|
|
</el-container>
|
|
</el-container>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
|
import { useRouter, useRoute } from 'vue-router'
|
|
import { DataLine, List, Expand, Fold, Setting, User, SwitchButton } from '@element-plus/icons-vue'
|
|
import { ADMIN_ROUTE_BASE } from '@/config/api.config'
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
const userRole = computed(() => localStorage.getItem('userRole'))
|
|
const userInfo = computed(() => JSON.parse(localStorage.getItem('userInfo')))
|
|
|
|
const handleLogout = () => {
|
|
localStorage.removeItem('token')
|
|
localStorage.removeItem('isAuthenticated')
|
|
localStorage.removeItem('userRole')
|
|
router.push('/login')
|
|
}
|
|
|
|
const isCollapse = ref(localStorage.getItem('sidebarCollapsed') === 'true')
|
|
|
|
const toggleSidebar = () => {
|
|
isCollapse.value = !isCollapse.value
|
|
localStorage.setItem('sidebarCollapsed', isCollapse.value)
|
|
}
|
|
|
|
// 添加移动端检测
|
|
const isMobile = computed(() => {
|
|
return window.innerWidth <= 768
|
|
})
|
|
|
|
// 监听窗口大小变化
|
|
const resizeHandler = () => {
|
|
isMobile.value = window.innerWidth <= 768
|
|
}
|
|
|
|
onMounted(() => {
|
|
window.addEventListener('resize', resizeHandler)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', resizeHandler)
|
|
})
|
|
|
|
// 监听路由变化,在移动端自动关闭侧边栏
|
|
watch(
|
|
() => route.path,
|
|
() => {
|
|
if (isMobile.value && !isCollapse.value) {
|
|
isCollapse.value = true
|
|
localStorage.setItem('sidebarCollapsed', 'true')
|
|
}
|
|
}
|
|
)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.layout-container {
|
|
height: 100vh;
|
|
}
|
|
|
|
.aside {
|
|
background-color: #001529;
|
|
transition: all 0.3s;
|
|
overflow: hidden;
|
|
position: relative;
|
|
z-index: 10;
|
|
}
|
|
|
|
.logo {
|
|
height: 60px;
|
|
padding: 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #002140;
|
|
overflow: hidden;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.logo-img {
|
|
width: 32px;
|
|
height: 32px;
|
|
margin-right: v-bind('isCollapse ? "0" : "12px"');
|
|
}
|
|
|
|
.logo-text {
|
|
color: white;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.header {
|
|
background-color: #fff;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20px;
|
|
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
|
}
|
|
|
|
.header-left {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.toggle-icon {
|
|
font-size: 20px;
|
|
cursor: pointer;
|
|
color: #666;
|
|
transition: transform 0.3s;
|
|
}
|
|
|
|
.header-right {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.user-info {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.username {
|
|
margin-left: 8px;
|
|
font-size: 14px;
|
|
color: #666;
|
|
}
|
|
|
|
.main-container {
|
|
background-color: #f0f2f5;
|
|
padding: 20px;
|
|
}
|
|
|
|
.el-menu {
|
|
border-right: none;
|
|
}
|
|
|
|
.el-menu-vertical {
|
|
height: calc(100% - 60px);
|
|
}
|
|
|
|
:deep(.el-menu-item) {
|
|
height: 50px;
|
|
line-height: 50px;
|
|
}
|
|
|
|
:deep(.el-menu-item.is-active) {
|
|
background-color: #1890ff !important;
|
|
}
|
|
|
|
:deep(.el-menu-item:hover) {
|
|
background-color: #001f3d !important;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item) {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
:deep(.el-dropdown-menu__item .el-icon) {
|
|
margin: 0;
|
|
}
|
|
|
|
/* 添加菜单折叠样式 */
|
|
:deep(.el-menu--collapse) {
|
|
width: 64px;
|
|
}
|
|
|
|
:deep(.el-menu--collapse .el-menu-item) {
|
|
padding: 0 20px !important;
|
|
}
|
|
|
|
/* 遮罩层过渡动画 */
|
|
.fade-enter-active,
|
|
.fade-leave-active {
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.fade-enter-from,
|
|
.fade-leave-to {
|
|
opacity: 0;
|
|
}
|
|
|
|
/* 移动端样式 */
|
|
@media screen and (max-width: 768px) {
|
|
.mobile-mask {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(255, 255, 255, 0.705);
|
|
z-index: 9;
|
|
backdrop-filter: blur(8px);
|
|
opacity: 1;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.mobile-mask[style*="display: none"] {
|
|
opacity: 0;
|
|
}
|
|
|
|
.mobile-aside {
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
z-index: 10;
|
|
box-shadow: 2px 0 8px rgba(0,0,0,0.15);
|
|
transform: translateX(0);
|
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.mobile-collapsed {
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
/* 调整主容器在侧边栏隐藏时的样式 */
|
|
.el-container:has(.mobile-aside) + .el-container {
|
|
margin-left: 0;
|
|
}
|
|
|
|
/* 调整头部样式 */
|
|
.header {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 8;
|
|
}
|
|
|
|
/* 调整主容器样式 */
|
|
.main-container {
|
|
padding: 10px;
|
|
min-height: calc(100vh - 60px); /* 60px 是头部高度 */
|
|
}
|
|
}
|
|
|
|
/* 平板端样式 */
|
|
@media screen and (min-width: 769px) and (max-width: 1024px) {
|
|
.aside {
|
|
position: relative;
|
|
}
|
|
}
|
|
</style> |