feat: 移动端响应式侧边栏抽屉布局
- 检测移动端设备(<768px)自动切换为抽屉式侧边栏 - 添加半透明遮罩层和滑入滑出动画 - 路由切换时自动关闭侧边栏 - 优化移动端导航体验
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
<template>
|
||||
<router-view v-if="route.meta.fullPage" />
|
||||
<el-container v-else class="app-layout">
|
||||
<el-aside :width="isCollapse ? 'var(--sidebar-collapsed)' : 'var(--sidebar-width)'" class="sidebar" :class="{ collapsed: isCollapse }">
|
||||
<AppSidebar :is-collapse="isCollapse" />
|
||||
<!-- Mobile backdrop -->
|
||||
<div v-if="isMobile && !isCollapse" class="mobile-backdrop" @click="isCollapse = true"></div>
|
||||
<el-aside
|
||||
:width="isCollapse && !isMobile ? 'var(--sidebar-collapsed)' : 'var(--sidebar-width)'"
|
||||
class="sidebar"
|
||||
:class="{ collapsed: isCollapse, 'mobile-drawer': isMobile, 'mobile-open': isMobile && !isCollapse }"
|
||||
>
|
||||
<AppSidebar :is-collapse="isCollapse && !isMobile" />
|
||||
</el-aside>
|
||||
<el-container class="main-container">
|
||||
<el-header class="app-header-bar" height="60px">
|
||||
<AppHeader :is-collapse="isCollapse" @toggle="isCollapse = !isCollapse" />
|
||||
<AppHeader :is-collapse="isCollapse" @toggle="handleToggle" />
|
||||
</el-header>
|
||||
<el-main class="app-main">
|
||||
<router-view v-slot="{ Component }">
|
||||
@@ -20,13 +26,34 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import AppSidebar from './AppSidebar.vue'
|
||||
import AppHeader from './AppHeader.vue'
|
||||
|
||||
const isCollapse = ref(false)
|
||||
const isMobile = ref(false)
|
||||
const route = useRoute()
|
||||
|
||||
function checkMobile() {
|
||||
isMobile.value = window.innerWidth < 768
|
||||
if (isMobile.value) isCollapse.value = true
|
||||
}
|
||||
|
||||
function handleToggle() {
|
||||
isCollapse.value = !isCollapse.value
|
||||
}
|
||||
|
||||
// Close drawer on route change (mobile)
|
||||
watch(() => route.path, () => {
|
||||
if (isMobile.value) isCollapse.value = true
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
checkMobile()
|
||||
window.addEventListener('resize', checkMobile)
|
||||
})
|
||||
onUnmounted(() => window.removeEventListener('resize', checkMobile))
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -89,4 +116,34 @@ const route = useRoute()
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
.mobile-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.sidebar.mobile-drawer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
z-index: 100;
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.sidebar.mobile-drawer.mobile-open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.app-header-bar {
|
||||
padding: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user