fix(frontend): 路由切换后保持侧边栏滚动位置

AppLayout 在每个页面组件内部包裹,路由切换时整个组件树(含
AppSidebar)被销毁重建,侧边栏滚动位置丢失。管理员菜单较长
或屏幕较矮时,每次点击靠下的菜单项后都要重新滚动。

在 appStore 增加 sidebarScrollTop 状态,AppSidebar 在
onBeforeUnmount 保存、onMounted 恢复滚动位置。

Fixes #3796
This commit is contained in:
li
2026-07-07 21:08:03 +08:00
parent 4bcb13f77f
commit c7e44a83ad
2 changed files with 19 additions and 2 deletions
+17 -2
View File
@@ -30,7 +30,7 @@
</div>
<!-- Navigation -->
<nav class="sidebar-nav scrollbar-hide">
<nav ref="sidebarNavRef" class="sidebar-nav scrollbar-hide">
<!-- Admin View: Admin menu first, then personal menu -->
<template v-if="isAdmin">
<!-- Admin Section -->
@@ -188,7 +188,7 @@
</template>
<script setup lang="ts">
import { computed, h, onMounted, ref, watch } from 'vue'
import { computed, h, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { useAdminSettingsStore, useAppStore, useAuthStore, useOnboardingStore } from '@/stores'
@@ -246,6 +246,7 @@ const { canUseBatchImage, refreshBatchImageAccess } = useBatchImageAccess()
const sidebarCollapsed = computed(() => appStore.sidebarCollapsed)
const mobileOpen = computed(() => appStore.mobileOpen)
const isAdmin = computed(() => authStore.isAdmin)
const sidebarNavRef = ref<HTMLElement | null>(null)
const isDark = ref(document.documentElement.classList.contains('dark'))
const homePath = computed(() => (isAdmin.value ? '/admin/dashboard' : '/dashboard'))
@@ -924,6 +925,20 @@ onMounted(() => {
if (isAdmin.value) {
adminSettingsStore.fetch()
}
// Restore sidebar scroll position after route change re-mounts the component
if (appStore.sidebarScrollTop > 0 && sidebarNavRef.value) {
void nextTick(() => {
if (sidebarNavRef.value) {
sidebarNavRef.value.scrollTop = appStore.sidebarScrollTop
}
})
}
})
onBeforeUnmount(() => {
if (sidebarNavRef.value) {
appStore.sidebarScrollTop = sidebarNavRef.value.scrollTop
}
})
</script>
+2
View File
@@ -19,6 +19,7 @@ export const useAppStore = defineStore('app', () => {
const sidebarCollapsed = ref<boolean>(false)
const mobileOpen = ref<boolean>(false)
const sidebarScrollTop = ref<number>(0)
const loading = ref<boolean>(false)
const toasts = ref<Toast[]>([])
@@ -409,6 +410,7 @@ export const useAppStore = defineStore('app', () => {
// State
sidebarCollapsed,
mobileOpen,
sidebarScrollTop,
loading,
toasts,