feat: enhance responsive design for sidebar and drawer in AppLayout
This commit is contained in:
@ -5,8 +5,48 @@ import { createGetRoutes } from '@/plugins/router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 侧边栏折叠状态
|
||||
// 响应式断点检测
|
||||
const isMobile = ref(false);
|
||||
const isTablet = ref(false);
|
||||
|
||||
// 检测屏幕尺寸
|
||||
const updateScreenSize = () => {
|
||||
const width = window.innerWidth;
|
||||
const wasMobile = isMobile.value;
|
||||
|
||||
isMobile.value = width < 768;
|
||||
isTablet.value = width >= 768 && width < 1024;
|
||||
|
||||
// 当从移动端切换到桌面端时,关闭抽屉并重置折叠状态
|
||||
if (wasMobile && !isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
collapsed.value = false;
|
||||
}
|
||||
// 当从桌面端切换到移动端时,关闭抽屉
|
||||
else if (!wasMobile && isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 侧边栏状态管理
|
||||
const collapsed = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
// 初始化屏幕尺寸检测
|
||||
onMounted(() => {
|
||||
updateScreenSize();
|
||||
window.addEventListener('resize', updateScreenSize);
|
||||
|
||||
// 移动端默认收起侧边栏
|
||||
if (isMobile.value) {
|
||||
collapsed.value = true;
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', updateScreenSize);
|
||||
});
|
||||
|
||||
// 菜单项类型定义
|
||||
type MenuItemWithRoute = MenuOption & {
|
||||
@ -124,6 +164,11 @@ const handleMenuSelect = (key: string, item: MenuOption) => {
|
||||
const routeName = menuRouteMap.get(key) || (item as MenuItemWithRoute).routeName;
|
||||
if (routeName) {
|
||||
router.push({ name: routeName as never });
|
||||
|
||||
// 移动端点击菜单项后自动收起侧边栏
|
||||
if (isMobile.value) {
|
||||
drawerVisible.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -144,15 +189,37 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// 切换侧边栏折叠状态
|
||||
const toggleCollapsed = () => {
|
||||
collapsed.value = !collapsed.value;
|
||||
// 切换侧边栏状态
|
||||
const toggleSidebar = () => {
|
||||
if (isMobile.value) {
|
||||
// 移动端使用抽屉模式
|
||||
drawerVisible.value = !drawerVisible.value;
|
||||
} else {
|
||||
// 桌面端使用折叠模式
|
||||
collapsed.value = !collapsed.value;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-layout has-sider>
|
||||
<n-layout :has-sider="!isMobile">
|
||||
<!-- 移动端抽屉 -->
|
||||
<n-drawer
|
||||
v-if="isMobile"
|
||||
v-model:show="drawerVisible"
|
||||
:width="280"
|
||||
placement="left"
|
||||
:trap-focus="false"
|
||||
:block-scroll="false"
|
||||
>
|
||||
<n-drawer-content title="菜单" :native-scrollbar="false">
|
||||
<n-menu :options="menuOptions" :value="selectedKey" @update:value="handleMenuSelect" />
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
|
||||
<!-- 桌面端侧边栏 -->
|
||||
<n-layout-sider
|
||||
v-if="!isMobile"
|
||||
:collapsed="collapsed"
|
||||
:native-scrollbar="false"
|
||||
bordered
|
||||
@ -174,30 +241,80 @@ const toggleCollapsed = () => {
|
||||
</n-layout-sider>
|
||||
|
||||
<n-layout>
|
||||
<n-layout-header bordered style="height: 64px; padding: 0 24px; display: flex; align-items: center">
|
||||
<n-button quaternary @click="toggleCollapsed" style="margin-right: 12px">
|
||||
<n-layout-header
|
||||
bordered
|
||||
:style="{
|
||||
height: '64px',
|
||||
padding: isMobile ? '0 16px' : '0 24px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}"
|
||||
>
|
||||
<n-button
|
||||
quaternary
|
||||
@click="toggleSidebar"
|
||||
:style="{
|
||||
marginRight: isMobile ? '8px' : '12px',
|
||||
padding: isMobile ? '8px' : '6px',
|
||||
}"
|
||||
:size="isMobile ? 'medium' : 'small'"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon size="18">
|
||||
<n-icon :size="isMobile ? 20 : 18">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path v-if="collapsed" fill="currentColor" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
|
||||
<path
|
||||
v-else
|
||||
v-if="!isMobile && collapsed"
|
||||
fill="currentColor"
|
||||
d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"
|
||||
/>
|
||||
<path
|
||||
v-else-if="!isMobile && !collapsed"
|
||||
fill="currentColor"
|
||||
d="M3 18h13v-2H3v2zm0-5h10v-2H3v2zm0-7v2h13V6H3zm18 9.59L17.42 12 21 8.41 19.59 7l-5 5 5 5L21 15.59z"
|
||||
/>
|
||||
<!-- 移动端始终显示菜单图标 -->
|
||||
<path v-else fill="currentColor" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
|
||||
</svg>
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-button>
|
||||
<span style="font-size: 18px; font-weight: 500">Vue TS Example</span>
|
||||
<span
|
||||
:style="{
|
||||
fontSize: isMobile ? '16px' : '18px',
|
||||
fontWeight: '500',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}"
|
||||
>
|
||||
Vue TS Example
|
||||
</span>
|
||||
</n-layout-header>
|
||||
|
||||
<n-layout-content content-style="padding: 24px;">
|
||||
<n-layout-content
|
||||
:content-style="{
|
||||
padding: isMobile ? '16px' : '24px',
|
||||
minHeight: 'calc(100vh - 64px - 72px)', // 减去头部和底部高度
|
||||
}"
|
||||
>
|
||||
<router-view />
|
||||
</n-layout-content>
|
||||
|
||||
<n-layout-footer bordered style="padding: 24px; text-align: center">
|
||||
<span style="color: var(--n-text-color-disabled)"> © 2025 Vue TS Example. All rights reserved. </span>
|
||||
<n-layout-footer
|
||||
bordered
|
||||
:style="{
|
||||
padding: isMobile ? '16px' : '24px',
|
||||
textAlign: 'center',
|
||||
}"
|
||||
>
|
||||
<span
|
||||
:style="{
|
||||
color: 'var(--n-text-color-disabled)',
|
||||
fontSize: isMobile ? '12px' : '14px',
|
||||
}"
|
||||
>
|
||||
© 2025 Vue TS Example. All rights reserved.
|
||||
</span>
|
||||
</n-layout-footer>
|
||||
</n-layout>
|
||||
</n-layout>
|
||||
|
Reference in New Issue
Block a user