fix: 优化移动端全屏侧栏
移动端侧栏改为全屏固定导航,关闭后释放全部正文宽度,并统一使用菜单图标入口。
This commit is contained in:
+84
-39
@@ -23,25 +23,21 @@ import {
|
||||
FolderOpen,
|
||||
FileText,
|
||||
Layers,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
Settings2
|
||||
Menu,
|
||||
Settings2,
|
||||
X
|
||||
} from '@lucide/vue'
|
||||
import { AppDialog } from './components/ui'
|
||||
import { useTheme } from './composables/useTheme'
|
||||
import ThemeToggle from './components/ui/ThemeToggle.vue'
|
||||
import { projectAccessKey, type ProjectAccess } from './features/projects/access'
|
||||
|
||||
/** 窄屏与桌面共用同一侧栏;窄屏展开时主栏右移,宽度保持折叠时的尺寸。 */
|
||||
/** 窄屏将侧栏改为全屏覆盖层,关闭后不再占用正文宽度。 */
|
||||
const NARROW_NAV = '(max-width: 800px)'
|
||||
/** 折叠侧栏宽度,与菜单 collapsed-width 对齐。 */
|
||||
const SIDER_COLLAPSED_WIDTH = 64
|
||||
/** 展开侧栏宽度,窄屏裁切动画以内层保持该宽度。 */
|
||||
/** 桌面端展开侧栏宽度。 */
|
||||
const SIDER_EXPANDED_WIDTH = 208
|
||||
/** 窄屏折叠侧栏宽度,给正文多留空间,仍覆盖 44px 点击区。 */
|
||||
const SIDER_COLLAPSED_WIDTH_NARROW = 48
|
||||
/** 窄屏展开侧栏宽度。 */
|
||||
const SIDER_EXPANDED_WIDTH_NARROW = 168
|
||||
/** 菜单图标尺寸,折叠/展开共用,避免切换时缩放。 */
|
||||
const NAV_ICON_SIZE = 20
|
||||
|
||||
@@ -51,14 +47,14 @@ const narrowMedia = window.matchMedia(NARROW_NAV)
|
||||
const narrow = ref(narrowMedia.matches)
|
||||
const collapsed = ref(narrow.value)
|
||||
const settingsOpen = ref(false)
|
||||
/** 折叠只裁切文字,不把菜单收成图标,宽屏与窄屏共用同一套宽度动画。 */
|
||||
/** 桌面折叠只裁切文字;移动端侧栏打开时始终显示完整菜单。 */
|
||||
const menuCollapsed = false
|
||||
/** 当前视口下的折叠侧栏宽度。 */
|
||||
const siderCollapsedWidth = computed(() => (narrow.value ? SIDER_COLLAPSED_WIDTH_NARROW : SIDER_COLLAPSED_WIDTH))
|
||||
/** 当前视口下的展开侧栏宽度。 */
|
||||
const siderExpandedWidth = computed(() => (narrow.value ? SIDER_EXPANDED_WIDTH_NARROW : SIDER_EXPANDED_WIDTH))
|
||||
/** Naive Menu 的桌面折叠宽度;移动端覆盖层不参与正文宽度计算。 */
|
||||
const siderCollapsedWidth = SIDER_COLLAPSED_WIDTH
|
||||
/** 侧栏展开宽度仅用于桌面布局。 */
|
||||
const siderExpandedWidth = SIDER_EXPANDED_WIDTH
|
||||
/** 折叠与展开共用的图标左偏移,避免切换时图标跳动。 */
|
||||
const navIconIndent = computed(() => siderCollapsedWidth.value / 2 - NAV_ICON_SIZE / 2)
|
||||
const navIconIndent = computed(() => (narrow.value ? 20 : siderCollapsedWidth / 2 - NAV_ICON_SIZE / 2))
|
||||
|
||||
/** 视口跨过断点时:进入窄屏收回侧栏。 */
|
||||
function syncNarrow(event: MediaQueryListEvent) {
|
||||
@@ -93,8 +89,11 @@ function onKeydown(event: KeyboardEvent) {
|
||||
window.addEventListener('keydown', onKeydown)
|
||||
onScopeDispose(() => window.removeEventListener('keydown', onKeydown))
|
||||
|
||||
/** 顶栏切换按钮的可访问名称。 */
|
||||
const navToggleLabel = computed(() => (collapsed.value ? '展开侧栏' : '折叠侧栏'))
|
||||
/** 顶栏菜单按钮在移动端使用更符合抽屉导航语义的可访问名称。 */
|
||||
const navToggleLabel = computed(() => {
|
||||
if (narrow.value) return collapsed.value ? '打开主菜单' : '关闭主菜单'
|
||||
return collapsed.value ? '展开侧栏' : '折叠侧栏'
|
||||
})
|
||||
const { preference, theme, overrides } = useTheme()
|
||||
const projectId = computed(() => String(route.params.projectId || ''))
|
||||
const projectAccess = shallowRef<ProjectAccess | null>(null)
|
||||
@@ -156,24 +155,40 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
'--admin-sider-expanded': siderExpandedWidth + 'px'
|
||||
}"
|
||||
>
|
||||
<!-- 以下是主导航侧栏:宽屏与窄屏共用同一份 -->
|
||||
<!-- 以下是主导航侧栏:桌面参与分栏,移动端固定覆盖整个视口。 -->
|
||||
<aside
|
||||
id="main-navigation"
|
||||
class="admin-sider"
|
||||
:class="{ 'is-collapsed': collapsed }"
|
||||
:style="{ width: (collapsed ? siderCollapsedWidth : siderExpandedWidth) + 'px' }"
|
||||
:style="{
|
||||
width: narrow ? '100%' : (collapsed ? siderCollapsedWidth : siderExpandedWidth) + 'px'
|
||||
}"
|
||||
aria-label="主导航"
|
||||
:aria-hidden="narrow && collapsed ? 'true' : undefined"
|
||||
:inert="narrow && collapsed ? true : undefined"
|
||||
>
|
||||
<!-- 以下是侧栏品牌:折叠裁切文字,图标位置保持不变 -->
|
||||
<RouterLink
|
||||
to="/projects"
|
||||
class="admin-brand"
|
||||
:class="{ 'is-collapsed': collapsed }"
|
||||
aria-label="短剧工作台首页"
|
||||
>
|
||||
<Clapperboard :size="narrow ? NAV_ICON_SIZE : 24" /><span
|
||||
>短剧工作台<small>SHORT DRAMA STUDIO</small></span
|
||||
<header class="admin-sider-header">
|
||||
<!-- 以下是侧栏品牌:桌面折叠裁切文字,移动端始终展示完整品牌。 -->
|
||||
<RouterLink
|
||||
to="/projects"
|
||||
class="admin-brand"
|
||||
:class="{ 'is-collapsed': collapsed }"
|
||||
aria-label="短剧工作台首页"
|
||||
>
|
||||
</RouterLink>
|
||||
<Clapperboard :size="narrow ? NAV_ICON_SIZE : 24" /><span
|
||||
>短剧工作台<small>SHORT DRAMA STUDIO</small></span
|
||||
>
|
||||
</RouterLink>
|
||||
<!-- 移动端全屏侧栏覆盖顶栏,因此在侧栏内部提供明确关闭入口。 -->
|
||||
<NButton
|
||||
quaternary
|
||||
class="icon-button mobile-sider-close"
|
||||
aria-label="关闭主菜单"
|
||||
@click="toggleNav"
|
||||
>
|
||||
<template #icon><X :size="20" /></template>
|
||||
</NButton>
|
||||
</header>
|
||||
<NScrollbar class="admin-nav-scroll">
|
||||
<NMenu
|
||||
:value="route.path"
|
||||
@@ -216,11 +231,10 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
:aria-label="navToggleLabel"
|
||||
:title="navToggleLabel"
|
||||
:aria-expanded="!collapsed"
|
||||
aria-controls="main-navigation"
|
||||
@click="toggleNav"
|
||||
>
|
||||
<template #icon
|
||||
><PanelLeftOpen v-if="collapsed" :size="18" /><PanelLeftClose v-else :size="18"
|
||||
/></template>
|
||||
<template #icon><Menu :size="20" /></template>
|
||||
</NButton>
|
||||
<span class="truncate text-sm">{{ route.meta.title || '工作空间' }}</span>
|
||||
</div>
|
||||
@@ -270,6 +284,12 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
@apply flex flex-col h-full w-[208px] shrink-0 overflow-hidden bg-(--app-subtle);
|
||||
transition: width 0.32s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
.admin-sider-header {
|
||||
@apply flex shrink-0 items-center overflow-hidden;
|
||||
}
|
||||
.mobile-sider-close {
|
||||
@apply hidden shrink-0;
|
||||
}
|
||||
.admin-sider.is-collapsed {
|
||||
@apply w-16;
|
||||
}
|
||||
@@ -334,17 +354,42 @@ const menuOptions = computed<MenuOption[]>(() => [
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.admin-sider {
|
||||
overflow: hidden;
|
||||
@apply fixed inset-0 z-50 overflow-hidden;
|
||||
width: 100% !important;
|
||||
transform: translateX(0);
|
||||
transition:
|
||||
transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
visibility 0s;
|
||||
}
|
||||
/* 收起后整块导航移出视口且禁止交互,让正文使用完整屏幕宽度。 */
|
||||
.admin-sider.is-collapsed {
|
||||
transform: translateX(-100%);
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
|
||||
visibility 0s 0.28s;
|
||||
}
|
||||
/* 主栏宽度锁在折叠态剩余空间,侧栏变宽时把主栏推到右侧而不是压窄。 */
|
||||
.admin-layout > .admin-main {
|
||||
flex: 0 0 auto;
|
||||
width: calc(100% - var(--admin-sider-collapsed));
|
||||
min-width: calc(100% - var(--admin-sider-collapsed));
|
||||
max-width: calc(100% - var(--admin-sider-collapsed));
|
||||
@apply w-full min-w-0 max-w-full flex-1;
|
||||
}
|
||||
.admin-nav-scroll.n-scrollbar,
|
||||
.admin-brand {
|
||||
min-width: 100%;
|
||||
}
|
||||
.admin-sider-header {
|
||||
@apply h-12;
|
||||
}
|
||||
.admin-brand {
|
||||
@apply h-12 justify-start px-3.5;
|
||||
@apply h-12 min-w-0 flex-1 justify-start px-3.5;
|
||||
}
|
||||
.admin-sider.is-collapsed .admin-brand span,
|
||||
.admin-sider.is-collapsed .n-menu-item-content-header,
|
||||
.admin-sider.is-collapsed .admin-settings-label {
|
||||
opacity: 1;
|
||||
}
|
||||
.mobile-sider-close {
|
||||
@apply flex mr-2;
|
||||
}
|
||||
.admin-topbar {
|
||||
@apply py-0 px-2.5;
|
||||
|
||||
Reference in New Issue
Block a user