feat: 移动端响应式侧边栏抽屉布局

- 检测移动端设备(<768px)自动切换为抽屉式侧边栏
- 添加半透明遮罩层和滑入滑出动画
- 路由切换时自动关闭侧边栏
- 优化移动端导航体验
This commit is contained in:
Zhang Jing Xuan
2026-07-31 17:44:18 +08:00
parent c9b5785301
commit ce153c1323
+61 -4
View File
@@ -1,12 +1,18 @@
<template> <template>
<router-view v-if="route.meta.fullPage" /> <router-view v-if="route.meta.fullPage" />
<el-container v-else class="app-layout"> <el-container v-else class="app-layout">
<el-aside :width="isCollapse ? 'var(--sidebar-collapsed)' : 'var(--sidebar-width)'" class="sidebar" :class="{ collapsed: isCollapse }"> <!-- Mobile backdrop -->
<AppSidebar :is-collapse="isCollapse" /> <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-aside>
<el-container class="main-container"> <el-container class="main-container">
<el-header class="app-header-bar" height="60px"> <el-header class="app-header-bar" height="60px">
<AppHeader :is-collapse="isCollapse" @toggle="isCollapse = !isCollapse" /> <AppHeader :is-collapse="isCollapse" @toggle="handleToggle" />
</el-header> </el-header>
<el-main class="app-main"> <el-main class="app-main">
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
@@ -20,13 +26,34 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, onMounted, onUnmounted, watch } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import AppSidebar from './AppSidebar.vue' import AppSidebar from './AppSidebar.vue'
import AppHeader from './AppHeader.vue' import AppHeader from './AppHeader.vue'
const isCollapse = ref(false) const isCollapse = ref(false)
const isMobile = ref(false)
const route = useRoute() 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> </script>
<style scoped> <style scoped>
@@ -89,4 +116,34 @@ const route = useRoute()
.fade-leave-to { .fade-leave-to {
opacity: 0; 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> </style>