feat: 完成 #2 布局组件开发与移动端适配

This commit is contained in:
root
2026-03-09 19:42:00 +08:00
parent 6d5e846e7f
commit 135d2f92b2
3 changed files with 104 additions and 3 deletions
+90 -3
View File
@@ -1,7 +1,13 @@
<template> <template>
<el-container class="layout-container"> <el-container class="layout-container">
<div
v-if="isMobile && mobileMenuVisible"
class="mobile-mask"
@click="closeMobileMenu"
></div>
<!-- 侧边栏 --> <!-- 侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '200px'" class="sidebar"> <el-aside :width="asideWidth" class="sidebar" :class="{ 'sidebar-mobile': isMobile }">
<div class="logo"> <div class="logo">
<span v-if="!isCollapse">管理后台</span> <span v-if="!isCollapse">管理后台</span>
<span v-else>后台</span> <span v-else>后台</span>
@@ -9,9 +15,10 @@
<el-menu <el-menu
:default-active="activeMenu" :default-active="activeMenu"
:collapse="isCollapse" :collapse="menuCollapse"
:unique-opened="true" :unique-opened="true"
router router
@select="handleMenuSelect"
> >
<el-menu-item <el-menu-item
v-for="route in menuRoutes" v-for="route in menuRoutes"
@@ -61,7 +68,7 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { ElMessageBox, ElMessage } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus'
import { Fold, Expand, User, ArrowDown } from '@element-plus/icons-vue' import { Fold, Expand, User, ArrowDown } from '@element-plus/icons-vue'
@@ -72,8 +79,27 @@ const route = useRoute()
const userStore = useUserStore() const userStore = useUserStore()
const isCollapse = ref(false) const isCollapse = ref(false)
const isMobile = ref(window.innerWidth < 768)
const mobileMenuVisible = ref(false)
const asideWidth = computed(() => {
if (isMobile.value) {
return mobileMenuVisible.value ? '200px' : '0px'
}
return isCollapse.value ? '64px' : '200px'
})
const menuCollapse = computed(() => {
if (isMobile.value) {
return false
}
return isCollapse.value
})
onMounted(async () => { onMounted(async () => {
window.addEventListener('resize', handleResize)
handleResize()
if (userStore.token && !userStore.userInfo) { if (userStore.token && !userStore.userInfo) {
try { try {
await userStore.fetchUserInfo() await userStore.fetchUserInfo()
@@ -83,6 +109,19 @@ onMounted(async () => {
} }
}) })
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
})
watch(
() => route.path,
() => {
if (isMobile.value) {
closeMobileMenu()
}
}
)
// 获取菜单路由(过滤掉隐藏的路由) // 获取菜单路由(过滤掉隐藏的路由)
const menuRoutes = computed(() => { const menuRoutes = computed(() => {
const routes = router.getRoutes() const routes = router.getRoutes()
@@ -104,9 +143,30 @@ const activeMenu = computed(() => {
// 切换侧边栏折叠状态 // 切换侧边栏折叠状态
const toggleCollapse = () => { const toggleCollapse = () => {
if (isMobile.value) {
mobileMenuVisible.value = !mobileMenuVisible.value
return
}
isCollapse.value = !isCollapse.value isCollapse.value = !isCollapse.value
} }
const closeMobileMenu = () => {
mobileMenuVisible.value = false
}
const handleMenuSelect = () => {
if (isMobile.value) {
closeMobileMenu()
}
}
const handleResize = () => {
isMobile.value = window.innerWidth < 768
if (!isMobile.value) {
mobileMenuVisible.value = false
}
}
// 处理下拉菜单命令 // 处理下拉菜单命令
const handleCommand = async (command) => { const handleCommand = async (command) => {
if (command === 'logout') { if (command === 'logout') {
@@ -133,11 +193,14 @@ const handleCommand = async (command) => {
.layout-container { .layout-container {
width: 100%; width: 100%;
height: 100vh; height: 100vh;
position: relative;
} }
.sidebar { .sidebar {
background: #304156; background: #304156;
transition: width 0.3s; transition: width 0.3s;
overflow: hidden;
z-index: 1001;
} }
.logo { .logo {
@@ -218,4 +281,28 @@ const handleCommand = async (command) => {
padding: 20px; padding: 20px;
overflow-y: auto; overflow-y: auto;
} }
.mobile-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 1000;
}
@media (max-width: 767px) {
.sidebar-mobile {
position: fixed;
left: 0;
top: 0;
bottom: 0;
}
.header {
padding: 0 12px;
}
.main-content {
padding: 12px;
}
}
</style> </style>
+6
View File
@@ -61,6 +61,12 @@ const routes = [
component: () => import('../views/expiry/index.vue'), component: () => import('../views/expiry/index.vue'),
meta: { title: '保质期管理', icon: 'Box' } meta: { title: '保质期管理', icon: 'Box' }
}, },
{
path: 'watermark',
name: 'Watermark',
component: () => import('../views/watermark/index.vue'),
meta: { title: '去水印管理', icon: 'Brush' }
},
{ {
path: 'settings', path: 'settings',
name: 'Settings', name: 'Settings',
+8
View File
@@ -0,0 +1,8 @@
<template>
<el-card>
<template #header>
<span>去水印管理</span>
</template>
<el-empty description="开发中,下一步对接去水印任务管理接口" />
</el-card>
</template>