优化左边栏移动端效果

This commit is contained in:
jingrow 2025-12-28 02:30:16 +08:00
parent 569e4d3e45
commit d49d265472
2 changed files with 163 additions and 30 deletions

View File

@ -2,27 +2,53 @@
<n-config-provider :theme-overrides="themeOverrides" class="h-full">
<div class="relative flex h-full flex-col">
<n-layout class="h-full" has-sider>
<!-- 移动端遮罩层 -->
<div
v-if="isMobile && !sidebarCollapsed && !isSignupFlow && !isHideSidebar && session.user"
class="mobile-overlay"
@click="sidebarCollapsed = true"
></div>
<n-layout-sider
v-if="!isSignupFlow && !$isMobile && !isHideSidebar && $session.user"
v-if="!isSignupFlow && !isHideSidebar && session.user"
bordered
collapse-mode="width"
:collapsed-width="64"
:width="240"
v-model:collapsed="sidebarCollapsed"
:show-trigger="true"
:show-trigger="!isMobile"
:responsive="true"
:breakpoint="768"
class="app-sidebar-sider"
@collapse="sidebarCollapsed = true"
@expand="sidebarCollapsed = false"
>
<AppSidebar :collapsed="sidebarCollapsed" />
<AppSidebar :collapsed="sidebarCollapsed" @menu-select="handleMenuSelect" />
</n-layout-sider>
<n-layout class="h-full">
<!-- 移动端顶部导航栏 -->
<div
v-if="isMobile && !isSignupFlow && !isHideSidebar && session.user"
class="mobile-header"
>
<div class="mobile-header-content">
<JLogo class="mobile-logo" />
<div class="mobile-user-info">
{{ teamUserText }}
</div>
<n-button
quaternary
circle
@click="sidebarCollapsed = false"
class="mobile-menu-button"
>
<template #icon>
<n-icon><MenuIcon /></n-icon>
</template>
</n-button>
</div>
</div>
<div class="w-full h-full overflow-auto" id="scrollContainer">
<MobileNav
v-if="!isSignupFlow && $isMobile && !isHideSidebar && $session.user"
/>
<div
v-if="
!isSignupFlow &&
@ -47,20 +73,19 @@
</template>
<script setup>
import { defineAsyncComponent, computed, watch, ref, provide, onMounted } from 'vue';
import { NLayout, NLayoutSider, NConfigProvider } from 'naive-ui';
import { defineAsyncComponent, computed, watch, ref, provide, onMounted, onUnmounted } from 'vue';
import { NLayout, NLayoutSider, NConfigProvider, NButton, NIcon } from 'naive-ui';
import { Toaster } from 'vue-sonner';
import { dialogs } from './utils/components';
import { useRoute } from 'vue-router';
import { getTeam } from './data/team';
import { session } from './data/session.js';
import JLogo from '@/components/icons/JLogo.vue';
import MenuIcon from '~icons/lucide/menu';
const AppSidebar = defineAsyncComponent(
() => import('./components/AppSidebar.vue'),
);
const MobileNav = defineAsyncComponent(
() => import('./components/MobileNav.vue'),
);
const route = useRoute();
const team = getTeam();
@ -72,7 +97,6 @@ const themeOverrides = {
textColor: '#4a5568',
padding: '6px 12px',
borderRadius: '8px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)',
},
Menu: {
// 使
@ -94,20 +118,47 @@ const themeOverrides = {
},
};
//
const isMobile = ref(false);
//
const sidebarCollapsed = ref(false);
//
const teamUserText = computed(() => {
if (team?.get?.loading) {
return '加载中...';
}
return team?.pg?.user || '';
});
//
onMounted(() => {
const checkMobile = () => {
if (window.innerWidth < 768) {
const wasMobile = isMobile.value;
isMobile.value = window.innerWidth < 768;
//
if (!wasMobile && isMobile.value) {
sidebarCollapsed.value = true;
}
};
onMounted(() => {
checkMobile();
window.addEventListener('resize', checkMobile);
});
onUnmounted(() => {
window.removeEventListener('resize', checkMobile);
});
// -
const handleMenuSelect = () => {
if (isMobile.value) {
sidebarCollapsed.value = true;
}
};
const isHideSidebar = computed(() => {
const alwaysHideSidebarRoutes = [
'Site Login',
@ -159,23 +210,15 @@ provide('session', session);
background: #fafafa !important;
border-right: 1px solid rgba(0, 0, 0, 0.06) !important;
/* 使用平滑的缓动函数,避免抖动 */
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.app-sidebar-sider:not(.n-layout-sider--collapsed) {
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.04) !important;
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
/* 触发器样式优化 - 平滑过渡 */
.app-sidebar-sider .n-layout-sider-trigger {
background: #fff !important;
border: 1px solid rgba(0, 0, 0, 0.08) !important;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
/* 使用平滑的缓动函数,避免抖动 */
transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
transition: background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
width: 32px !important;
height: 32px !important;
border-radius: 50% !important;
@ -186,7 +229,6 @@ provide('session', session);
.app-sidebar-sider .n-layout-sider-trigger:hover {
background: #f5f5f5 !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}
.app-sidebar-sider .n-layout-sider-trigger:active {
@ -220,4 +262,93 @@ provide('session', session);
height: 100% !important;
min-height: 100vh !important;
}
/* 移动端遮罩层 */
.mobile-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 998;
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 移动端顶部导航栏 */
.mobile-header {
position: sticky;
top: 0;
z-index: 999;
background: #fff;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.mobile-header-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
gap: 12px;
}
.mobile-logo {
width: 28px;
height: 28px;
flex-shrink: 0;
}
.mobile-user-info {
flex: 1;
font-size: 14px;
font-weight: 500;
color: #1a1a1a;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mobile-menu-button {
flex-shrink: 0;
}
/* 移动端侧边栏样式优化 */
@media (max-width: 767px) {
.app-sidebar-sider {
position: fixed !important;
top: 0;
left: 0;
height: 100vh !important;
z-index: 999 !important;
width: 280px !important;
max-width: 85vw;
transform: translateX(-100%);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.app-sidebar-sider:not(.n-layout-sider--collapsed) {
transform: translateX(0) !important;
}
/* 移动端主内容区域占满全宽 */
.n-layout {
margin-left: 0 !important;
}
}
/* 桌面端保持原有样式 */
@media (min-width: 768px) {
.app-sidebar-sider {
position: relative !important;
transform: none !important;
}
.mobile-header {
display: none;
}
.mobile-overlay {
display: none;
}
}
</style>

View File

@ -76,8 +76,8 @@ export default {
default: false,
},
},
emits: ['update:collapsed'],
setup(props) {
emits: ['update:collapsed', 'menu-select'],
setup(props, { emit }) {
const route = useRoute();
const router = useRouter();
const instance = getCurrentInstance();
@ -214,6 +214,8 @@ export default {
console.error('Navigation error:', err);
}
});
//
emit('menu-select');
};