feat: 全局移动端适配 AdminLayout 汉堡抽屉 + 67 页通用规则

This commit is contained in:
guoju0808
2026-08-26 11:13:12 +00:00
parent d2fe5a4035
commit 8a08c8378f
5 changed files with 513 additions and 4 deletions
+106
View File
@@ -4,13 +4,24 @@
v-if="!route.meta?.hideMenu"
width="220px"
class="aside"
:class="{ 'aside-mobile-open': mobileAsideOpen }"
>
<transition name="aside-fade">
<div v-if="mobileAsideOpen" class="aside-mask" @click="mobileAsideOpen = false"></div>
</transition>
<div class="aside-logo">
<div class="logo-icon"><img src="/logo.png" alt="logo" /></div>
<div class="logo-text">
<div class="logo-name">合规管理系统</div>
<div class="logo-role">{{ roleLabel }}</div>
</div>
<!-- 手机端关闭抽屉按钮 -->
<button class="aside-close" type="button" aria-label="关闭菜单" @click="mobileAsideOpen = false">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="6" y1="6" x2="18" y2="18"/>
<line x1="6" y1="18" x2="18" y2="6"/>
</svg>
</button>
</div>
<el-menu :default-active="route.path" router class="aside-menu">
<template v-for="m in menu" :key="m.path">
@@ -36,6 +47,13 @@
<el-container>
<el-header v-if="!route.meta?.hideMenu" class="topbar" height="56px">
<div class="topbar-left">
<button class="hamburger" type="button" aria-label="打开菜单" @click="mobileAsideOpen = true">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
</button>
<el-icon class="topbar-back" @click="$router.back()"><ArrowLeft /></el-icon>
<span class="page-title-inline">{{ headerTitle }}</span>
</div>
@@ -93,6 +111,9 @@ const router = useRouter()
const store = useUserStore()
const role = computed(() => route.meta?.role || store.role)
// 移动端侧栏抽屉状态 (汉堡按钮触发)
const mobileAsideOpen = ref(false)
// 顶栏标题: 页面动态标题 (如会议 新建/修改/复制) 优先, 否则回退 route.meta.title
const dynamicTitle = usePageTitle()
const headerTitle = computed(() => dynamicTitle.value || route.meta?.title || '工作台')
@@ -164,6 +185,8 @@ onMounted(() => {
stopWatchRoute = watch(() => route.path, () => {
loadInitialUnread()
loadExpertAuditStatus()
// 移动端侧栏抽屉: 路由变化自动关闭 (避免点完菜单后抽屉还遮住主内容)
mobileAsideOpen.value = false
})
})
onBeforeUnmount(() => {
@@ -404,4 +427,87 @@ const onCommand = (cmd) => { if (cmd === 'logout') logout() }
.main { background: var(--brand-slate-50); padding: 16px 20px; min-height: calc(100vh - 56px); }
/* 手机页面 (hideMenu): 白底 + 0 padding, 让 form 直接撑满 */
.main-full { background: #fff; padding: 0; }
/* ========== 移动端适配 (≤768px) ========== */
/* 顶栏: padding 收窄 + 简化用户区 */
@media (max-width: 768px) {
.admin-layout {
flex-direction: column;
}
/* 侧栏: 桌面常驻, 手机转抽屉 (默认隐藏在屏幕左外) */
.aside {
position: fixed !important;
top: 0;
left: 0;
width: 240px !important;
max-width: 80vw;
height: 100vh;
z-index: 2000;
background: #fff;
transform: translateX(-100%);
transition: transform 0.25s ease;
box-shadow: none;
}
.aside.aside-mobile-open {
transform: translateX(0);
box-shadow: 4px 0 20px rgba(0, 0, 0, 0.15);
}
.aside-close {
display: inline-flex !important;
margin-left: auto;
background: transparent;
border: none;
color: var(--el-text-color-secondary);
padding: 6px;
border-radius: 4px;
cursor: pointer;
}
.aside-close:hover { background: var(--brand-slate-100); }
.aside-logo { padding: 16px 16px 14px !important; }
/* 顶栏: padding 收窄 + 隐藏门户首页 + 用户名只显示图标 */
.topbar { padding: 0 12px !important; height: 52px !important; }
.topbar-portal { display: none !important; } /* 顶部"门户首页"链接手机端隐藏, 改在抽屉 */
.topbar-portal + .el-divider { display: none !important; }
.page-title-inline { font-size: 14px !important; max-width: 50vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-name { padding: 0 6px !important; }
.user-name > span:not(.caret):not(.user-icon) { display: none !important; } /* 隐藏用户名文字, 只留头像图标 */
.user-name .user-icon { font-size: 18px !important; }
/* 汉堡按钮: 桌面隐藏, 手机显示 */
.hamburger {
display: inline-flex !important;
align-items: center;
justify-content: center;
background: transparent;
border: none;
color: var(--el-text-color-regular);
padding: 6px;
border-radius: 4px;
cursor: pointer;
transition: background 0.18s, color 0.18s;
}
.hamburger:hover { background: var(--brand-slate-100); color: var(--brand-primary); }
/* 主体: padding 收窄 */
.main { padding: 12px !important; min-height: calc(100vh - 52px); }
}
/* aside-close 桌面隐藏 */
.aside-close { display: none; }
.hamburger { display: none; }
/* 移动端侧栏抽屉打开时的蒙层 (放在 layout 顶层, 透过 admin-layout 主结构显示) */
@media (max-width: 768px) {
.admin-layout { position: relative; }
.aside-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
z-index: 1999;
}
}
.aside-fade-enter-active, .aside-fade-leave-active { transition: opacity 0.2s; }
.aside-fade-enter-from, .aside-fade-leave-to { opacity: 0; }
</style>