mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
Merge pull request #3803 from fengshao1227/fix/sidebar-scroll-position-persist
fix(frontend): 路由切换后保持侧边栏滚动位置
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -19,6 +19,29 @@ describe('AppSidebar custom SVG styles', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('AppSidebar scroll position persistence', () => {
|
||||
it('binds a template ref to the sidebar nav element', () => {
|
||||
expect(componentSource).toContain('ref="sidebarNavRef"')
|
||||
expect(componentSource).toContain('sidebar-nav')
|
||||
})
|
||||
|
||||
it('declares sidebarNavRef in script setup', () => {
|
||||
expect(componentSource).toContain("const sidebarNavRef = ref<HTMLElement | null>(null)")
|
||||
})
|
||||
|
||||
it('saves scroll position on beforeUnmount', () => {
|
||||
expect(componentSource).toContain('onBeforeUnmount')
|
||||
expect(componentSource).toContain('appStore.sidebarScrollTop')
|
||||
expect(componentSource).toContain('sidebarNavRef.value.scrollTop')
|
||||
})
|
||||
|
||||
it('restores scroll position on mount', () => {
|
||||
expect(componentSource).toContain('onMounted')
|
||||
expect(componentSource).toContain('appStore.sidebarScrollTop')
|
||||
expect(componentSource).toContain('nextTick')
|
||||
})
|
||||
})
|
||||
|
||||
describe('AppSidebar header styles', () => {
|
||||
it('does not clip the version badge dropdown', () => {
|
||||
const sidebarHeaderBlockMatch = styleSource.match(/\.sidebar-header\s*\{[\s\S]*?\n {2}\}/)
|
||||
|
||||
@@ -147,6 +147,17 @@ describe('useAppStore', () => {
|
||||
expect(store.sidebarCollapsed).toBe(false)
|
||||
})
|
||||
|
||||
it('sidebarScrollTop 默认为 0 且可读写', () => {
|
||||
const store = useAppStore()
|
||||
expect(store.sidebarScrollTop).toBe(0)
|
||||
|
||||
store.sidebarScrollTop = 256
|
||||
expect(store.sidebarScrollTop).toBe(256)
|
||||
|
||||
store.sidebarScrollTop = 0
|
||||
expect(store.sidebarScrollTop).toBe(0)
|
||||
})
|
||||
|
||||
it('toggleMobileSidebar 切换移动端状态', () => {
|
||||
const store = useAppStore()
|
||||
expect(store.mobileOpen).toBe(false)
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user